1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Surface editor - Added auto-arrange

This commit is contained in:
SpoinkyNL 2020-12-06 23:40:53 +01:00
parent f05740a5f1
commit 220222d102
8 changed files with 333 additions and 68 deletions

View File

@ -44,6 +44,11 @@ namespace Artemis.Core.Services
/// <param name="surface">The surface entity to delete, may not be the active surface entity</param>
void DeleteSurfaceConfiguration(ArtemisSurface surface);
/// <summary>
/// Applies auto-arranging logic to the current active surface
/// </summary>
void AutoArrange();
/// <summary>
/// Occurs when the active device entity has been changed
/// </summary>

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace Artemis.Core.Services.Models
@ -7,18 +8,108 @@ namespace Artemis.Core.Services.Models
{
public SurfaceArrangement()
{
Devices = new List<SurfaceArrangementDevice>();
Types = new List<SurfaceArrangementType>();
}
public List<SurfaceArrangementDevice> Devices { get; }
public List<SurfaceArrangementType> Types { get; }
internal static SurfaceArrangement GetDefaultArrangement()
{
SurfaceArrangement arrangement = new SurfaceArrangement();
arrangement.Devices.Add(new SurfaceArrangementDevice(null, RGBDeviceType.Keyboard, ArrangementPosition.Right));
SurfaceArrangementType keypad = new SurfaceArrangementType(RGBDeviceType.Keypad);
keypad.Configurations.Add(new SurfaceArrangementConfiguration(null, HorizontalArrangementPosition.Equal, VerticalArrangementPosition.Equal, 20));
arrangement.Types.Add(keypad);
SurfaceArrangementType keyboard = new SurfaceArrangementType(RGBDeviceType.Keyboard);
keyboard.Configurations.Add(new SurfaceArrangementConfiguration(keypad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 20));
arrangement.Types.Add(keyboard);
SurfaceArrangementType mousepad = new SurfaceArrangementType(RGBDeviceType.Mousepad);
mousepad.Configurations.Add(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 10));
arrangement.Types.Add(mousepad);
SurfaceArrangementType mouse = new SurfaceArrangementType(RGBDeviceType.Mouse);
mouse.Configurations.Add(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
mouse.Configurations.Add(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Center, 100));
arrangement.Types.Add(mouse);
SurfaceArrangementType headset = new SurfaceArrangementType(RGBDeviceType.Headset);
headset.Configurations.Add(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Bottom, 100));
arrangement.Types.Add(headset);
SurfaceArrangementType headsetStand = new SurfaceArrangementType(RGBDeviceType.HeadsetStand);
headsetStand.Configurations.Add(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
headsetStand.Configurations.Add(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Top, 100));
headsetStand.Configurations.Add(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Top, 100));
arrangement.Types.Add(headsetStand);
SurfaceArrangementType mainboard = new SurfaceArrangementType(RGBDeviceType.Mainboard);
mainboard.Configurations.Add(new SurfaceArrangementConfiguration(mousepad, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
mainboard.Configurations.Add(new SurfaceArrangementConfiguration(mouse, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
mainboard.Configurations.Add(new SurfaceArrangementConfiguration(keyboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 500));
arrangement.Types.Add(mainboard);
SurfaceArrangementType cooler = new SurfaceArrangementType(RGBDeviceType.Cooler);
cooler.Configurations.Add(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
arrangement.Types.Add(cooler);
SurfaceArrangementType dram = new SurfaceArrangementType(RGBDeviceType.DRAM);
dram.Configurations.Add(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Left, VerticalArrangementPosition.Equal, 10));
dram.Configurations.Add(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
arrangement.Types.Add(dram);
SurfaceArrangementType graphicsCard = new SurfaceArrangementType(RGBDeviceType.GraphicsCard);
graphicsCard.Configurations.Add(new SurfaceArrangementConfiguration(cooler, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
graphicsCard.Configurations.Add(new SurfaceArrangementConfiguration(dram, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Bottom, 10));
graphicsCard.Configurations.Add(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Center, VerticalArrangementPosition.Center, 0));
arrangement.Types.Add(graphicsCard);
SurfaceArrangementType fan = new SurfaceArrangementType(RGBDeviceType.Fan);
fan.Configurations.Add(new SurfaceArrangementConfiguration(mainboard, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
arrangement.Types.Add(fan);
SurfaceArrangementType ledStripe = new SurfaceArrangementType(RGBDeviceType.LedStripe);
ledStripe.Configurations.Add(new SurfaceArrangementConfiguration(fan, HorizontalArrangementPosition.Right, VerticalArrangementPosition.Equal, 100));
arrangement.Types.Add(ledStripe);
SurfaceArrangementType speaker = new SurfaceArrangementType(RGBDeviceType.Speaker);
arrangement.Types.Add(speaker);
return arrangement;
}
public void Arrange(ArtemisSurface surface)
{
foreach (ArtemisDevice surfaceDevice in surface.Devices)
{
surfaceDevice.X = 0;
surfaceDevice.X = 0;
surfaceDevice.ApplyToRgbDevice();
}
foreach (SurfaceArrangementType surfaceArrangementType in Types)
surfaceArrangementType.Arrange(surface);
// See if we need to move the surface to keep X and Y values positive
double x = surface.Devices.Min(d => d.RgbDevice.Location.X);
double y = surface.Devices.Min(d => d.RgbDevice.Location.Y);
if (x < 0)
{
foreach (ArtemisDevice surfaceDevice in surface.Devices)
{
surfaceDevice.X += x * -1;
surfaceDevice.ApplyToRgbDevice();
}
}
if (y < 0)
{
foreach (ArtemisDevice surfaceDevice in surface.Devices)
{
surfaceDevice.Y += y * -1;
surfaceDevice.ApplyToRgbDevice();
}
}
}
}
}
}

View File

@ -0,0 +1,99 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace Artemis.Core.Services.Models
{
internal class SurfaceArrangementConfiguration
{
public SurfaceArrangementConfiguration(SurfaceArrangementType? anchor, HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition,
int margin)
{
Anchor = anchor;
HorizontalPosition = horizontalPosition;
VerticalPosition = verticalPosition;
MarginLeft = margin;
MarginTop = margin;
MarginRight = margin;
MarginBottom = margin;
}
public SurfaceArrangementConfiguration(SurfaceArrangementType? anchor, HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition,
int marginLeft, int marginTop, int marginRight, int marginBottom)
{
Anchor = anchor;
HorizontalPosition = horizontalPosition;
VerticalPosition = verticalPosition;
MarginLeft = marginLeft;
MarginTop = marginTop;
MarginRight = marginRight;
MarginBottom = marginBottom;
}
public SurfaceArrangementType? Anchor { get; }
public HorizontalArrangementPosition HorizontalPosition { get; }
public VerticalArrangementPosition VerticalPosition { get; }
public int MarginLeft { get; }
public int MarginTop { get; }
public int MarginRight { get; }
public int MarginBottom { get; }
public bool Apply(List<ArtemisDevice> devices, ArtemisSurface surface)
{
if (Anchor != null && !Anchor.HasDevices(surface))
return false;
// Start at the edge of the anchor, if there is no anchor start at any device
Point startPoint = Anchor?.GetEdge(HorizontalPosition, VerticalPosition, surface) ??
new SurfaceArrangementType(RGBDeviceType.All).GetEdge(HorizontalPosition, VerticalPosition, surface);
// Stack multiple devices of the same type vertically if they are wider than they are tall
bool stackVertically = devices.Average(d => d.RgbDevice.Size.Width) >= devices.Average(d => d.RgbDevice.Size.Height);
ArtemisDevice? previous = null;
foreach (ArtemisDevice artemisDevice in devices)
{
if (previous != null)
{
if (stackVertically)
{
artemisDevice.X = previous.X;
artemisDevice.Y = previous.RgbDevice.Location.Y + previous.RgbDevice.Size.Height + MarginTop / 2.0;
}
else
{
artemisDevice.X = previous.RgbDevice.Location.X + previous.RgbDevice.Size.Width + MarginLeft / 2.0;
artemisDevice.Y = previous.Y;
}
}
else
{
artemisDevice.X = HorizontalPosition switch
{
HorizontalArrangementPosition.Left => startPoint.X - artemisDevice.RgbDevice.Size.Width - MarginRight,
HorizontalArrangementPosition.Right => startPoint.X + MarginLeft,
HorizontalArrangementPosition.Center => startPoint.X - artemisDevice.RgbDevice.Size.Width / 2,
HorizontalArrangementPosition.Equal => startPoint.X,
_ => artemisDevice.X
};
artemisDevice.Y = VerticalPosition switch
{
VerticalArrangementPosition.Top => startPoint.Y - artemisDevice.RgbDevice.Size.Height - MarginBottom,
VerticalArrangementPosition.Bottom => startPoint.Y + MarginTop,
VerticalArrangementPosition.Center => startPoint.Y - artemisDevice.RgbDevice.Size.Height / 2,
VerticalArrangementPosition.Equal => startPoint.Y,
_ => artemisDevice.X
};
}
artemisDevice.ApplyToRgbDevice();
previous = artemisDevice;
}
return true;
}
}
}

View File

@ -1,43 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace Artemis.Core.Services.Models
{
internal class SurfaceArrangementDevice
{
public SurfaceArrangementDevice? Anchor { get; }
public RGBDeviceType DeviceType { get; }
public ArrangementPosition Position { get; }
public SurfaceArrangementDevice(SurfaceArrangementDevice? anchor, RGBDeviceType deviceType, ArrangementPosition position)
{
Anchor = anchor;
DeviceType = deviceType;
Position = position;
}
public void Apply(ArtemisSurface surface)
{
List<ArtemisDevice> devices = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
ArtemisDevice? previous = null;
foreach (ArtemisDevice artemisDevice in devices)
{
if (previous != null)
{
}
previous = artemisDevice;
}
}
}
internal enum ArrangementPosition
{
Left,
Right,
Top,
Bottom,
Center
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RGB.NET.Core;
namespace Artemis.Core.Services.Models
{
internal class SurfaceArrangementType
{
public SurfaceArrangementType(RGBDeviceType deviceType)
{
DeviceType = deviceType;
Configurations = new List<SurfaceArrangementConfiguration>();
}
public RGBDeviceType DeviceType { get; }
public List<SurfaceArrangementConfiguration> Configurations { get; }
public SurfaceArrangementConfiguration? AppliedConfiguration { get; set; }
public bool HasDevices(ArtemisSurface surface)
{
return surface.Devices.Any(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType);
}
public void Arrange(ArtemisSurface surface)
{
List<ArtemisDevice> devices = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType).ToList();
if (!devices.Any())
return;
AppliedConfiguration = null;
foreach (SurfaceArrangementConfiguration configuration in Configurations)
{
bool applied = configuration.Apply(devices, surface);
if (applied)
{
AppliedConfiguration = configuration;
return;
}
}
// If nothing applied fall back to just basing on whatever is the furthers to the right
SurfaceArrangementConfiguration fallback = new SurfaceArrangementConfiguration(
null,
HorizontalArrangementPosition.Right,
VerticalArrangementPosition.Equal,
10
);
fallback.Apply(devices, surface);
AppliedConfiguration = fallback;
}
public Point GetEdge(HorizontalArrangementPosition horizontalPosition, VerticalArrangementPosition verticalPosition, ArtemisSurface surface)
{
List<ArtemisDevice> devices = surface.Devices.Where(d => d.RgbDevice.DeviceInfo.DeviceType == DeviceType || DeviceType == RGBDeviceType.All).ToList();
if (!devices.Any())
return new Point();
double x = horizontalPosition switch
{
HorizontalArrangementPosition.Left => devices.Min(d => d.RgbDevice.Location.X) - (AppliedConfiguration?.MarginLeft ?? 0.0),
HorizontalArrangementPosition.Right => devices.Max(d => d.RgbDevice.Location.X + d.RgbDevice.Size.Width) + (AppliedConfiguration?.MarginRight ?? 0.0),
HorizontalArrangementPosition.Center => devices.First().RgbDevice.DeviceRectangle.Center.X,
HorizontalArrangementPosition.Equal => devices.First().RgbDevice.Location.X,
_ => throw new ArgumentOutOfRangeException(nameof(horizontalPosition), horizontalPosition, null)
};
double y = verticalPosition switch
{
VerticalArrangementPosition.Top => devices.Min(d => d.RgbDevice.Location.Y) - (AppliedConfiguration?.MarginTop ?? 0.0),
VerticalArrangementPosition.Bottom => devices.Max(d => d.RgbDevice.Location.Y + d.RgbDevice.Size.Height) + (AppliedConfiguration?.MarginBottom ?? 0.0),
VerticalArrangementPosition.Center => devices.First().RgbDevice.DeviceRectangle.Center.Y,
VerticalArrangementPosition.Equal => devices.First().RgbDevice.Location.Y,
_ => throw new ArgumentOutOfRangeException(nameof(verticalPosition), verticalPosition, null)
};
return new Point(x, y);
}
}
internal enum HorizontalArrangementPosition
{
Left,
Right,
Equal,
Center
}
internal enum VerticalArrangementPosition
{
Top,
Bottom,
Equal,
Center
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.DeviceProviders;
using Artemis.Core.Services.Models;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
using RGB.NET.Core;
@ -202,13 +203,11 @@ namespace Artemis.Core.Services
#region AutoLayout
public void AutoLayout()
public void AutoArrange()
{
// Phase one, bottom layer
// Keyboard
// Phase two, top layer
SurfaceArrangement surfaceArrangement = SurfaceArrangement.GetDefaultArrangement();
surfaceArrangement.Arrange(ActiveSurface);
UpdateSurfaceConfiguration(ActiveSurface, true);
}
#endregion

View File

@ -178,23 +178,32 @@
</Path.Fill>
</Path>
<StackPanel Orientation="Vertical" VerticalAlignment="Bottom" HorizontalAlignment="Right"
Margin="0, 0, 15, 15">
<Slider Margin="0,0,14,0"
Orientation="Vertical"
Minimum="10"
Maximum="400"
Height="100"
FocusVisualStyle="{x:Null}"
Value="{Binding PanZoomViewModel.ZoomPercentage}"
Style="{StaticResource MaterialDesignDiscreteSlider}" />
<Button Command="{s:Action ResetZoomAndPan}"
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0, 0, 15, 15">
<Button Command="{s:Action AutoArrange}"
Style="{StaticResource MaterialDesignFloatingActionMiniButton}"
HorizontalAlignment="Right"
ToolTip="Reset zoom &amp; position">
<materialDesign:PackIcon Kind="ImageFilterCenterFocus" Height="24" Width="24" />
ToolTip="Apply auto-arrange"
VerticalAlignment="Bottom">
<materialDesign:PackIcon Kind="Wand" Height="24" Width="24" />
</Button>
<StackPanel Orientation="Vertical">
<Slider Margin="0,0,14,0"
Orientation="Vertical"
Minimum="10"
Maximum="400"
Height="100"
FocusVisualStyle="{x:Null}"
Value="{Binding PanZoomViewModel.ZoomPercentage}"
Style="{StaticResource MaterialDesignDiscreteSlider}" />
<Button Command="{s:Action ResetZoomAndPan}"
Style="{StaticResource MaterialDesignFloatingActionMiniButton}"
HorizontalAlignment="Right"
ToolTip="Reset zoom &amp; position">
<materialDesign:PackIcon Kind="ImageFilterCenterFocus" Height="24" Width="24" />
</Button>
</StackPanel>
</StackPanel>
</Grid>
</materialDesign:Card>

View File

@ -114,6 +114,16 @@ namespace Artemis.UI.Screens.SurfaceEditor
return config;
}
public async Task AutoArrange()
{
bool confirmed = await _dialogService.ShowConfirmDialog("Auto-arrange layout", "Are you sure you want to auto-arrange your layout? Your current settings will be overwritten.");
if (!confirmed)
return;
_surfaceService.AutoArrange();
}
private void LoadWorkspaceSettings()
{
SurfaceListWidth = _settingsService.GetSetting("SurfaceEditor.SurfaceListWidth", new GridLength(300.0));