using System.Linq; using Artemis.Core; using SkiaSharp; namespace Artemis.UI.Shared.Extensions; /// /// Provides utilities when working with layers in UI elements. /// public static class LayerExtensions { /// /// Returns the layer's bounds in real coordinates. /// public static SKRect GetLayerBounds(this Layer layer) { if (!layer.Leds.Any()) return SKRect.Empty; return new SKRect( layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.X), layer.Leds.Min(l => l.RgbLed.AbsoluteBoundary.Location.Y), layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.X + l.RgbLed.AbsoluteBoundary.Size.Width), layer.Leds.Max(l => l.RgbLed.AbsoluteBoundary.Location.Y + l.RgbLed.AbsoluteBoundary.Size.Height) ); } /// /// Returns the layer's anchor in real coordinates. /// public static SKPoint GetLayerAnchorPosition(this Layer layer, SKPoint? positionOverride = null) { SKRect layerBounds = GetLayerBounds(layer); SKPoint positionProperty = layer.Transform.Position.CurrentValue; if (positionOverride != null) positionProperty = positionOverride.Value; // Start at the top left of the shape SKPoint position = new(layerBounds.Left, layerBounds.Top); // Apply translation position.X += positionProperty.X * layerBounds.Width; position.Y += positionProperty.Y * layerBounds.Height; return position; } /// /// Returns an absolute and scaled rectangular path for the given layer in real coordinates. /// public static SKPath GetLayerPath(this Layer layer, bool includeTranslation, bool includeScale, bool includeRotation) { SKRect layerBounds = GetLayerBounds(layer); SKMatrix transform = layer.GetTransformMatrix(false, includeTranslation, includeScale, includeRotation, layerBounds); SKPath path = new(); path.AddRect(layerBounds); path.Transform(transform); return path; } /// /// Returns a new point normalized to 0.0-1.0 /// public static SKPoint GetNormalizedPoint(this Layer layer, SKPoint point, bool absolute) { SKRect bounds = GetLayerBounds(layer); if (absolute) return new SKPoint( 100 / bounds.Width * (point.X - bounds.Left) / 100, 100 / bounds.Height * (point.Y - bounds.Top) / 100 ); return new SKPoint( 100 / bounds.Width * point.X / 100, 100 / bounds.Height * point.Y / 100 ); } /// /// Returns the offset from the given point to the closest sides of the layer's shape bounds /// public static SKPoint GetDragOffset(this Layer layer, SKPoint dragStart) { SKRect bounds = layer.GetLayerPath(true, true, false).Bounds; SKPoint anchor = layer.GetLayerAnchorPosition(); float xOffset = 0f, yOffset = 0f; // X offset if (dragStart.X < anchor.X) xOffset = bounds.Left - dragStart.X; else if (dragStart.X > anchor.X) xOffset = bounds.Right - dragStart.X; // Y offset if (dragStart.Y < anchor.Y) yOffset = bounds.Top - dragStart.Y; else if (dragStart.Y > anchor.Y) yOffset = bounds.Bottom - dragStart.Y; return new SKPoint(xOffset, yOffset); } }