From 253bf24816020b806e7b24bd9a89f032569aa518 Mon Sep 17 00:00:00 2001 From: Aytackydln Date: Wed, 11 Dec 2024 01:18:34 +0100 Subject: [PATCH] made PercentLayer non-render layer --- .../EffectsEngine/IEffectLayer.cs | 5 - .../EffectsEngine/NoRenderLayer.cs | 74 ----- .../EffectsEngine/ZoneKeyPercentDrawer.cs | 288 ++++++++++++++++++ .../Settings/Layers/PercentLayerHandler.cs | 58 ++-- 4 files changed, 308 insertions(+), 117 deletions(-) create mode 100644 Project-Aurora/Project-Aurora/EffectsEngine/ZoneKeyPercentDrawer.cs diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs index c6316d412..320ad8088 100644 --- a/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/IEffectLayer.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Drawing; using AuroraRgb.Settings; using Common.Devices; @@ -48,10 +47,6 @@ public interface EffectLayer : IDisposable /// Itself void Set(KeySequence sequence, ref readonly Color color); - void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, - double total, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, - bool flashReversed = false, bool blinkBackground = false); - /// /// + Operator, sums two EffectLayer together. /// diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs index ac0c5eaf2..4383ecec7 100644 --- a/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs +++ b/Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using AuroraRgb.Settings; -using AuroraRgb.Utils; using Common.Devices; using Common.Utils; @@ -126,78 +124,6 @@ private void SetOver(DeviceKeys key, ref readonly Color foregroundColor) Set(key, in newColor); } - /// - /// Draws a percent effect on the layer bitmap using an array of DeviceKeys keys and solid colors. - /// - /// The foreground color, used as a "Progress bar color" - /// The current progress value - /// The maxiumum progress value - public void PercentEffect(Color foregroundColor, Color backgroundColor, IReadOnlyList keys, double value, - double total, PercentEffectType percentEffectType = PercentEffectType.Progressive, double flashPast = 0.0, - bool flashReversed = false, bool blinkBackground = false) - { - var progressTotal = value / total; - if (progressTotal < 0.0) - progressTotal = 0.0; - else if (progressTotal > 1.0) - progressTotal = 1.0; - - var progress = progressTotal * keys.Count; - - if (flashPast > 0.0 && ((flashReversed && progressTotal >= flashPast) || (!flashReversed && progressTotal <= flashPast))) - { - var percent = Math.Sin(Time.GetMillisecondsSinceEpoch() % 1000.0D / 1000.0D * Math.PI); - if (blinkBackground) - backgroundColor = ColorUtils.BlendColors(backgroundColor, Color.Empty, percent); - else - foregroundColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, percent); - } - - if (percentEffectType is PercentEffectType.Highest_Key or PercentEffectType.Highest_Key_Blend && keys.Count > 0) - { - var activeKey = (int)Math.Ceiling(Math.Clamp(value, 0, 1) / (total / keys.Count)) - 1; - var col = percentEffectType == PercentEffectType.Highest_Key ? - foregroundColor : ColorUtils.BlendColors(backgroundColor, foregroundColor, progressTotal); - for (var i = 0; i < keys.Count; i++) - { - if (i != activeKey) - { - Set(keys[i], Color.Transparent); - } - } - Set(keys[activeKey], in col); - - } - else - { - for (var i = 0; i < keys.Count; i++) - { - var currentKey = keys[i]; - - switch (percentEffectType) - { - case PercentEffectType.AllAtOnce: - Set(currentKey, ColorUtils.BlendColors(in backgroundColor, in foregroundColor, progressTotal)); - break; - case PercentEffectType.Progressive_Gradual: - if (i == (int)progress) - { - var percent = progress - i; - Set(currentKey, ColorUtils.BlendColors(in backgroundColor, in foregroundColor, percent)); - } - else if (i < (int)progress) - Set(currentKey, foregroundColor); - else - Set(currentKey, backgroundColor); - break; - default: - Set(currentKey, i < (int) progress ? foregroundColor : backgroundColor); - break; - } - } - } - } - public void Exclude(KeySequence sequence) { _excludedZoneKeysCache.SetSequence(sequence); diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/ZoneKeyPercentDrawer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/ZoneKeyPercentDrawer.cs new file mode 100644 index 000000000..b88f43f90 --- /dev/null +++ b/Project-Aurora/Project-Aurora/EffectsEngine/ZoneKeyPercentDrawer.cs @@ -0,0 +1,288 @@ +using System; +using System.Drawing; +using System.Linq; +using AuroraRgb.Settings; +using AuroraRgb.Utils; +using Common.Devices; + +namespace AuroraRgb.EffectsEngine; + +public class ZoneKeyPercentDrawer(EffectLayer effectLayer) +{ + public void PercentEffect(Color foregroundColor, Color backgroundColor, KeySequence sequence, double value, + double total = 1.0D, PercentEffectType percentEffectType = PercentEffectType.Progressive, + double flashPast = 0.0, bool flashReversed = false, bool blinkBackground = false) + { + var zoneKeysCache = new ZoneKeysCache(); + zoneKeysCache.SetSequence(sequence); + var keys = zoneKeysCache.GetKeys(); + + if (sequence.Type == KeySequenceType.FreeForm) + { + PercentEffectOnFreeForm(foregroundColor, backgroundColor, sequence.Freeform, keys, value, total, + percentEffectType, flashPast, flashReversed, blinkBackground); + } + else + { + // Fallback to previous implementation for regular sequences + PercentEffectOnKeys(foregroundColor, backgroundColor, keys, value, total, + percentEffectType, flashPast, flashReversed, blinkBackground); + } + } + + private void PercentEffectOnFreeForm(Color foregroundColor, Color backgroundColor, FreeFormObject freeform, + DeviceKeys[] keys, double value, double total, PercentEffectType percentEffectType, + double flashPast, bool flashReversed, bool blinkBackground) + { + switch (percentEffectType) + { + case PercentEffectType.AllAtOnce: + { + var progressTotal = Math.Clamp(value / total, 0.0, 1.0); + + // Apply flash effect if needed + if (flashPast > 0.0 && ((flashReversed && progressTotal >= flashPast) || (!flashReversed && progressTotal <= flashPast))) + { + var percent = Math.Sin(Time.GetMillisecondsSinceEpoch() % 1000.0D / 1000.0D * Math.PI); + if (blinkBackground) + backgroundColor = ColorUtils.BlendColors(backgroundColor, Color.Empty, percent); + else + foregroundColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, percent); + } + + var keyColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, progressTotal); + + effectLayer.Set(keys, in keyColor); + } + return; + case PercentEffectType.Progressive: + case PercentEffectType.Progressive_Gradual: + PercentEffectOnFreeForm(foregroundColor, backgroundColor, freeform, keys, value, total, flashPast, flashReversed, blinkBackground, effectLayer, + percentEffectType == PercentEffectType.Progressive_Gradual); + return; + } + } + + private static void PercentEffectOnFreeForm(Color foregroundColor, Color backgroundColor, FreeFormObject freeform, + DeviceKeys[] keys, double value, double total, + double flashPast, bool flashReversed, bool blinkBackground, EffectLayer effectLayer, bool gradual) + { + // Calculate progress + var progressTotal = Math.Clamp(value / total, 0.0, 1.0); + + // Apply flash effect if needed + if (flashPast > 0.0 && ((flashReversed && progressTotal >= flashPast) || (!flashReversed && progressTotal <= flashPast))) + { + var percent = Math.Sin(Time.GetMillisecondsSinceEpoch() % 1000.0D / 1000.0D * Math.PI); + if (blinkBackground) + backgroundColor = ColorUtils.BlendColors(backgroundColor, Color.Empty, percent); + else + foregroundColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, percent); + } + + // Calculate freeform object's world coordinates and corners + var freeformCorners = GetFreeFormCorners(freeform, progressTotal); + + // Iterate through keys and determine their color based on their position + foreach (var key in keys) + { + // Get key's rectangle in canvas coordinates + ref readonly var keyRect = ref Effects.Canvas.GetRectangle(key); + + // Create key corners + var keyCorners = new[] + { + new PointF(keyRect.Left, keyRect.Bottom), + new PointF(keyRect.Right, keyRect.Bottom), + new PointF(keyRect.Right, keyRect.Top), + new PointF(keyRect.Left, keyRect.Top) + }; + + // Calculate coverage + var coverageRatio = CalculateKeyCoverage(freeformCorners, keyCorners); + + Color keyColor; + if (gradual) + { + keyColor = ColorUtils.BlendColors( + backgroundColor, + foregroundColor, + Math.Min(coverageRatio, 1.0) + ); + } + else + { + var isKeyCovered = coverageRatio >= 1f - float.Epsilon; + keyColor = isKeyCovered ? foregroundColor : backgroundColor; + } + + effectLayer.Set(key, in keyColor); + } + } + + /// + /// Calculates the coverage ratio of a key by a freeform object. + /// + /// Corners of the freeform object + /// Corners of the key + /// A ratio between 0 and 1 representing the key's coverage + private static float CalculateKeyCoverage(PointF[] freeformCorners, PointF[] keyCorners) + { + // Count how many key corners are inside the freeform + var containedCorners = keyCorners.Count(corner => PointInRectangle(corner, freeformCorners)); + + // Calculate coverage based on number of contained corners + return (float)containedCorners / keyCorners.Length; + } + + /// + /// Gets the corners of a FreeFormObject in canvas coordinates + /// + private static PointF[] GetFreeFormCorners(FreeFormObject freeForm, double progress) + { + // Convert FreeForm coordinates to canvas coordinates + var xPos = (freeForm.X + Effects.Canvas.GridBaselineX) * Effects.Canvas.EditorToCanvasWidth; + var yPos = (freeForm.Y + Effects.Canvas.GridBaselineY) * Effects.Canvas.EditorToCanvasHeight; + var width = freeForm.Width * Effects.Canvas.EditorToCanvasWidth * progress; + var height = freeForm.Height * Effects.Canvas.EditorToCanvasHeight; + + var left = xPos; + var right = xPos + width; + var top = yPos; + var bottom = yPos + height; + + // Calculate the center point of rotation + var centerX = xPos + width / 2; + var centerY = yPos + height / 2; + + // Convert angle to radians + var angleRad = freeForm.Angle * Math.PI / 180; + var cos = Math.Cos(angleRad); + var sin = Math.Sin(angleRad); + + // Calculate the corners of the rotated rectangle + return + [ + TransformPoint(left, top, centerX, centerY, cos, sin), + TransformPoint(right, top, centerX, centerY, cos, sin), + TransformPoint(right, bottom, centerX, centerY, cos, sin), + TransformPoint(left, bottom, centerX, centerY, cos, sin) + ]; + } + + /// + /// Transforms a point by rotating it around a center point + /// + private static PointF TransformPoint(double x, double y, double centerX, double centerY, double cos, double sin) + { + // Translate point to origin + var translatedX = x - centerX; + var translatedY = y - centerY; + + // Rotate + var rotatedX = translatedX * cos - translatedY * sin; + var rotatedY = translatedX * sin + translatedY * cos; + + // Translate back + return new PointF( + (float)(rotatedX + centerX), + (float)(rotatedY + centerY) + ); + } + + /// + /// Checks if a point is inside a rectangle + /// + private static bool PointInRectangle(PointF point, PointF[] rectangleCorners) + { + // Translate the point and rectangle so that the rectangle's first corner is at the origin + var translatedPoint = new PointF( + point.X - rectangleCorners[0].X, + point.Y - rectangleCorners[0].Y + ); + + // Calculate the vectors of the rectangle's sides + var vector1 = new PointF( + rectangleCorners[1].X - rectangleCorners[0].X, + rectangleCorners[1].Y - rectangleCorners[0].Y + ); + + var vector2 = new PointF( + rectangleCorners[3].X - rectangleCorners[0].X, + rectangleCorners[3].Y - rectangleCorners[0].Y + ); + + // Calculate dot products to determine if point is inside + var dot1 = translatedPoint.X * vector1.X + translatedPoint.Y * vector1.Y; + var dot2 = translatedPoint.X * vector2.X + translatedPoint.Y * vector2.Y; + + // Check if the point is within the rectangle's side lengths + return dot1 >= 0 && dot1 <= vector1.X * vector1.X + vector1.Y * vector1.Y && + dot2 >= 0 && dot2 <= vector2.X * vector2.X + vector2.Y * vector2.Y; + } + + private void PercentEffectOnKeys(Color foregroundColor, Color backgroundColor, DeviceKeys[] keys, double value, + double total, PercentEffectType percentEffectType, double flashPast, bool flashReversed, bool blinkBackground) + { + // Previous implementation for non-freeform sequences + var progressTotal = Math.Clamp(value / total, 0.0, 1.0); + var progress = progressTotal * keys.Length; + + // Flash effect logic + if (flashPast > 0.0 && ((flashReversed && progressTotal >= flashPast) || (!flashReversed && progressTotal <= flashPast))) + { + var percent = Math.Sin(Time.GetMillisecondsSinceEpoch() % 1000.0D / 1000.0D * Math.PI); + if (blinkBackground) + backgroundColor = ColorUtils.BlendColors(backgroundColor, Color.Empty, percent); + else + foregroundColor = ColorUtils.BlendColors(backgroundColor, foregroundColor, percent); + } + + + switch (percentEffectType) + { + case PercentEffectType.AllAtOnce: + effectLayer.Set(keys, ColorUtils.BlendColors(in backgroundColor, in foregroundColor, progressTotal)); + break; + case PercentEffectType.Progressive_Gradual: + for (var i = 0; i < keys.Length; i++) + { + var currentKey = keys[i]; + if (i == (int)progress) + { + var percent = progress - i; + var blendColor = ColorUtils.BlendColors(in backgroundColor, in foregroundColor, percent); + effectLayer.Set(currentKey, in blendColor); + } + else if (i < (int)progress) + effectLayer.Set(currentKey, in foregroundColor); + else + effectLayer.Set(currentKey, in backgroundColor); + } + + break; + case PercentEffectType.Progressive: + for (var i = 0; i < keys.Length; i++) + { + var currentKey = keys[i]; + effectLayer.Set(currentKey, i < (int)progress ? foregroundColor : backgroundColor); + } + + break; + case PercentEffectType.Highest_Key: + { + effectLayer.Set(keys, in backgroundColor); + var highestKey = (int)progress; + effectLayer.Set(keys[highestKey], in foregroundColor); + break; + } + case PercentEffectType.Highest_Key_Blend: + { + var highestKey = (int)progress; + var blendColor = ColorUtils.BlendColors(in backgroundColor, in foregroundColor, progress); + effectLayer.Set(keys[highestKey], in blendColor); + break; + } + } + } +} \ No newline at end of file diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs index c6331acf7..c529c1c2a 100644 --- a/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs +++ b/Project-Aurora/Project-Aurora/Settings/Layers/PercentLayerHandler.cs @@ -1,6 +1,4 @@ -using System.ComponentModel; -using System.Globalization; -using System.Linq; +using System.Globalization; using System.Windows.Controls; using AuroraRgb.EffectsEngine; using AuroraRgb.Profiles; @@ -86,65 +84,49 @@ public override void Default() } } -public class PercentLayerHandler() : LayerHandler("PercentLayer") +public class PercentLayerHandler() : LayerHandler("PercentLayer") where TProperty : PercentLayerHandlerProperties { private double _value; - private readonly NoRenderLayer NoRenderLayer = new(); - public override EffectLayer Render(IGameState gameState) { var keySequence = Properties.Sequence; - EffectLayer layer = keySequence.Type switch - { - KeySequenceType.Sequence => NoRenderLayer, - _ => EffectLayer, - }; - - + if (Invalidated) { - layer.Clear(); Invalidated = false; _value = -1; } var value = Properties.Logic?._Value ?? gameState.GetNumber(Properties.VariablePath); - if (MathUtils.NearlyEqual(_value, value, 0.000001)) + if (MathUtils.NearlyEqual(_value, value, 0.000001) && !Invalidated) { - return layer; + return EffectLayer; } _value = value; - + var maxvalue = Properties.Logic?._MaxValue ?? gameState.GetNumber(Properties.MaxVariablePath); - if (keySequence.Type == KeySequenceType.Sequence) - { - NoRenderLayer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, keySequence.Keys, value, maxvalue, - Properties.PercentType, Properties.BlinkThreshold, Properties.BlinkDirection, Properties.BlinkBackground); - return NoRenderLayer; - } - EffectLayer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, keySequence, value, maxvalue, + EffectLayer.Clear(); + var percentDrawer = new ZoneKeyPercentDrawer(EffectLayer); + percentDrawer.PercentEffect(Properties.PrimaryColor, Properties.SecondaryColor, keySequence, value, maxvalue, Properties.PercentType, Properties.BlinkThreshold, Properties.BlinkDirection, Properties.BlinkBackground); return EffectLayer; } public override void SetApplication(Application profile) { - if (profile != null) - { - if (!double.TryParse(Properties.VariablePath.GsiPath, CultureInfo.InvariantCulture, out _) && - !string.IsNullOrWhiteSpace(Properties.VariablePath.GsiPath) && - !profile.ParameterLookup.IsValidParameter(Properties.VariablePath.GsiPath) - ) - Properties.VariablePath = VariablePath.Empty; - - if (!double.TryParse(Properties.MaxVariablePath.GsiPath, CultureInfo.InvariantCulture, out _) && - !string.IsNullOrWhiteSpace(Properties.MaxVariablePath.GsiPath) && - !profile.ParameterLookup.IsValidParameter(Properties.MaxVariablePath.GsiPath) - ) - Properties.MaxVariablePath = VariablePath.Empty; - } + if (!double.TryParse(Properties.VariablePath.GsiPath, CultureInfo.InvariantCulture, out _) && + !string.IsNullOrWhiteSpace(Properties.VariablePath.GsiPath) && + !profile.ParameterLookup.IsValidParameter(Properties.VariablePath.GsiPath) + ) + Properties.VariablePath = VariablePath.Empty; + + if (!double.TryParse(Properties.MaxVariablePath.GsiPath, CultureInfo.InvariantCulture, out _) && + !string.IsNullOrWhiteSpace(Properties.MaxVariablePath.GsiPath) && + !profile.ParameterLookup.IsValidParameter(Properties.MaxVariablePath.GsiPath) + ) + Properties.MaxVariablePath = VariablePath.Empty; base.SetApplication(profile); } }