Skip to content

Commit

Permalink
add Freeform key detection to NoRenderLayer.cs
Browse files Browse the repository at this point in the history
  • Loading branch information
Aytackydln committed Nov 1, 2024
1 parent 73a6bb1 commit 04cba7b
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 2 deletions.
11 changes: 9 additions & 2 deletions Project-Aurora/Project-Aurora/EffectsEngine/NoRenderLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public sealed class NoRenderLayer : EffectLayer
// TODO optimize a lot by reducing the result of this
public DeviceKeys[] ActiveKeys => Effects.Canvas.Keys;
private DeviceKeys[] AllKeys => Effects.Canvas.Keys;

private readonly ZoneKeysCache _zoneKeysCache = new();

public NoRenderLayer()
{
_zoneKeysCache.KeysChanged += (_, _) => Clear();
}

public void Fill(ref readonly Color color)
{
Expand Down Expand Up @@ -97,8 +104,7 @@ private IEnumerable<DeviceKeys> GetKeys(KeySequence sequence)

private IEnumerable<DeviceKeys> GetKeys(FreeFormObject sequenceFreeform)
{
//TODO implement getting keys inside the rectangle
return [];
return _zoneKeysCache.GetKeys(sequenceFreeform);
}

public EffectLayer Add(EffectLayer other)
Expand Down Expand Up @@ -259,6 +265,7 @@ public void Close()

public void Dispose()
{
_zoneKeysCache.Dispose();
}

private Color GetCurrentColor(DeviceKeys deviceKey)
Expand Down
237 changes: 237 additions & 0 deletions Project-Aurora/Project-Aurora/EffectsEngine/ZoneKeysCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
using System;
using System.Collections.Generic;
using AuroraRgb.Settings;
using Common.Devices;

namespace AuroraRgb.EffectsEngine;

public sealed class ZoneKeysCache : IDisposable
{
public event EventHandler? KeysChanged;

private DeviceKeys[]? _zoneKeys;
private FreeFormObject? _lastFreeForm;

public ZoneKeysCache()
{
Effects.CanvasChanged += EffectsOnCanvasChanged;
}

public DeviceKeys[] GetKeys(FreeFormObject freeFormObject)
{
// Return cached keys if the FreeFormObject hasn't changed
if (_zoneKeys != null && _lastFreeForm != null && _lastFreeForm.Equals(freeFormObject))
{
return _zoneKeys;
}

// Store the new FreeFormObject and subscribe to its changes
if (_lastFreeForm != null)
{
_lastFreeForm.ValuesChanged -= FreeFormObjectOnValuesChanged;
}

_lastFreeForm = freeFormObject;
freeFormObject.ValuesChanged += FreeFormObjectOnValuesChanged;

// Calculate and cache the keys
_zoneKeys = CalculateKeys(freeFormObject);
KeysChanged?.Invoke(this, EventArgs.Empty);
return _zoneKeys;
}

private DeviceKeys[] CalculateKeys(FreeFormObject freeForm)
{
var canvas = Effects.Canvas;

var matchingKeys = new List<DeviceKeys>();
var editorToCanvasWidth = canvas.EditorToCanvasWidth;
var editorToCanvasHeight = canvas.EditorToCanvasHeight;

// Convert FreeForm coordinates to canvas coordinates
var canvasX = (freeForm.X + canvas.CanvasGridProperties.GridBaselineX) * editorToCanvasWidth;
var canvasY = (freeForm.Y + canvas.CanvasGridProperties.GridBaselineY) * editorToCanvasHeight;
var canvasWidth = freeForm.Width * editorToCanvasWidth;
var canvasHeight = freeForm.Height * editorToCanvasHeight;

// Calculate the center point of rotation
var centerX = canvasX + canvasWidth / 2;
var centerY = canvasY + canvasHeight / 2;

// Convert angle to radians
var angleRad = freeForm.Angle * (float)Math.PI / 180f;
var cos = (float)Math.Cos(angleRad);
var sin = (float)Math.Sin(angleRad);

// Calculate the corners of the rotated rectangle
var corners = new[]
{
TransformPoint(canvasX, canvasY, centerX, centerY, cos, sin),
TransformPoint(canvasX + canvasWidth, canvasY, centerX, centerY, cos, sin),
TransformPoint(canvasX + canvasWidth, canvasY + canvasHeight, centerX, centerY, cos, sin),
TransformPoint(canvasX, canvasY + canvasHeight, centerX, centerY, cos, sin)
};

foreach (var key in canvas.Keys)
{
ref readonly var rect = ref canvas.GetRectangle(key);

// Create corners for the key rectangle
var keyCorners = new[]
{
new PointF(rect.Left, rect.Bottom),
new PointF(rect.Left + rect.Width, rect.Bottom),
new PointF(rect.Left + rect.Width, rect.Bottom + rect.Height),
new PointF(rect.Left, rect.Bottom + rect.Height)
};

if (PolygonContainsPolygon(corners, keyCorners))
{
matchingKeys.Add(key);
}
}

return matchingKeys.ToArray();
}

private readonly struct PointF(float x, float y)
{
public readonly float X = x;
public readonly float Y = y;
}

private static PointF TransformPoint(float x, float y, float centerX, float centerY, float cos, float 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(
rotatedX + centerX,
rotatedY + centerY
);
}

private static bool PolygonContainsPolygon(PointF[] container, PointF[] contained)
{
// First, check if any point of the contained polygon is outside the container
foreach (var point in contained)
{
if (!PointInPolygon(point, container))
{
return false;
}
}

return true;
}

private static bool PointInPolygon(PointF point, PointF[] polygon)
{
bool inside = false;
int j = polygon.Length - 1;

for (int i = 0; i < polygon.Length; i++)
{
if (((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y)) &&
(point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) /
(polygon[j].Y - polygon[i].Y) + polygon[i].X))
{
inside = !inside;
}
j = i;
}

return inside;
}

private static bool PolygonsIntersect(PointF[] polygon1, PointF[] polygon2)
{
// Use the Separating Axis Theorem (SAT) to detect intersection
// Check all edges of both polygons as potential separating axes

// Check edges of polygon1
for (var i = 0; i < polygon1.Length; i++)
{
var j = (i + 1) % polygon1.Length;
var edge = new PointF(
polygon1[j].X - polygon1[i].X,
polygon1[j].Y - polygon1[i].Y
);
var axis = new PointF(-edge.Y, edge.X); // Normal to the edge

if (HasSeparatingAxis(axis, polygon1, polygon2))
{
return false;
}
}

// Check edges of polygon2
for (var i = 0; i < polygon2.Length; i++)
{
var j = (i + 1) % polygon2.Length;
var edge = new PointF(
polygon2[j].X - polygon2[i].X,
polygon2[j].Y - polygon2[i].Y
);
var axis = new PointF(-edge.Y, edge.X); // Normal to the edge

if (HasSeparatingAxis(axis, polygon1, polygon2))
{
return false;
}
}

return true;
}

private static bool HasSeparatingAxis(PointF axis, PointF[] polygon1, PointF[] polygon2)
{
var (min1, max1) = ProjectPolygon(axis, polygon1);
var (min2, max2) = ProjectPolygon(axis, polygon2);

return max1 < min2 || max2 < min1;
}

private static (float Min, float Max) ProjectPolygon(PointF axis, PointF[] polygon)
{
var min = float.MaxValue;
var max = float.MinValue;

foreach (var point in polygon)
{
var projection = (point.X * axis.X + point.Y * axis.Y) /
(axis.X * axis.X + axis.Y * axis.Y);

min = Math.Min(min, projection);
max = Math.Max(max, projection);
}

return (min, max);
}

private void FreeFormObjectOnValuesChanged(object? sender, FreeFormChangedEventArgs e)
{
Invalidate();
}

private void EffectsOnCanvasChanged(object? sender, EventArgs e)
{
Invalidate();
}

private void Invalidate()
{
_zoneKeys = null;
}

public void Dispose()
{
Effects.CanvasChanged -= EffectsOnCanvasChanged;
}
}

0 comments on commit 04cba7b

Please sign in to comment.