1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

UI - Many misc fixes I can't remember

Profile editor - Removed layer outline, increasing performance by a lot
Device visualizer - Streamlined custom
Device providers - Added debug device provider
ASUS - Added Maximus X Hero thanks @copystring
Plugin core - Added PerLedLayerBrush
Noise layer - Converted to PerLedLayerBrush, increasing performance and quality
This commit is contained in:
SpoinkyNL 2020-08-02 00:20:25 +02:00
parent 3000d4607a
commit aa7c914b92
29 changed files with 767 additions and 277 deletions

View File

@ -124,7 +124,8 @@ namespace Artemis.Core.Models.Profile.Conditions
StaticConditionLambda = null;
CompiledStaticConditionLambda = null;
if (PredicateType == PredicateType.Dynamic)
// If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
CreateStaticExpression();

View File

@ -156,8 +156,10 @@ namespace Artemis.Core.Models.Profile
// Iterate the children in reverse because the first layer must be rendered last to end up on top
for (var index = Children.Count - 1; index > -1; index--)
{
folderCanvas.Save();
var profileElement = Children[index];
profileElement.Render(deltaTime, folderCanvas, _folderBitmap.Info);
folderCanvas.Restore();
}
var targetLocation = Path.Bounds.Location;

View File

@ -425,12 +425,14 @@ namespace Artemis.Core.Models.Profile
OnRenderPropertiesUpdated();
}
internal SKPoint GetLayerAnchorPosition(SKPath layerPath)
internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false)
{
var positionProperty = Transform.Position.CurrentValue;
// Start at the center of the shape
var position = new SKPoint(layerPath.Bounds.MidX, layerPath.Bounds.MidY);
var position = zeroBased
? new SKPoint(layerPath.Bounds.MidX - layerPath.Bounds.Left, layerPath.Bounds.MidY - layerPath.Bounds.Top)
: new SKPoint(layerPath.Bounds.MidX, layerPath.Bounds.MidY);
// Apply translation
position.X += positionProperty.X * layerPath.Bounds.Width;
@ -444,17 +446,46 @@ namespace Artemis.Core.Models.Profile
/// layer translations out
/// </summary>
/// <param name="path"></param>
public void ExcludePathFromTranslation(SKPath path)
public void IncludePathInTranslation(SKPath path, bool zeroBased)
{
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
var anchorPosition = GetLayerAnchorPosition(Path);
var anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
var anchorProperty = Transform.AnchorPoint.CurrentValue;
// Translation originates from the unscaled center of the shape and is tied to the anchor
var x = anchorPosition.X - Bounds.MidX - anchorProperty.X * Bounds.Width;
var y = anchorPosition.Y - Bounds.MidY - anchorProperty.Y * Bounds.Height;
var x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
var y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
if (General.FillType == LayerFillType.Stretch)
{
path.Transform(SKMatrix.MakeTranslation(x, y));
path.Transform(SKMatrix.MakeScale((sizeProperty.Width / 100f), (sizeProperty.Height / 100f), anchorPosition.X, anchorPosition.Y));
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
}
else
{
path.Transform(SKMatrix.MakeTranslation(x, y));
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
}
}
/// <summary>
/// Excludes the provided path from the translations applied to the layer by applying translations that cancel the
/// layer translations out
/// </summary>
public void ExcludePathFromTranslation(SKPath path, bool zeroBased)
{
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
var anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
var anchorProperty = Transform.AnchorPoint.CurrentValue;
// Translation originates from the unscaled center of the shape and is tied to the anchor
var x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
var y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
var reversedXScale = 1f / (sizeProperty.Width / 100f);
var reversedYScale = 1f / (sizeProperty.Height / 100f);
@ -472,6 +503,40 @@ namespace Artemis.Core.Models.Profile
}
}
/// <summary>
/// Excludes the provided canvas from the translations applied to the layer by applying translations that cancel the
/// layer translations out
/// </summary>
/// <returns>The number of transformations applied</returns>
public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased)
{
var sizeProperty = Transform.Scale.CurrentValue;
var rotationProperty = Transform.Rotation.CurrentValue;
var anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
var anchorProperty = Transform.AnchorPoint.CurrentValue;
// Translation originates from the unscaled center of the shape and is tied to the anchor
var x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
var y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
var reversedXScale = 1f / (sizeProperty.Width / 100f);
var reversedYScale = 1f / (sizeProperty.Height / 100f);
if (General.FillType == LayerFillType.Stretch)
{
canvas.Translate(x * -1, y * -1);
canvas.Scale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y);
canvas.RotateDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y);
return 3;
}
canvas.RotateDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y);
canvas.Translate(x * -1, y * -1);
return 2;
}
#endregion
#region LED management

View File

@ -30,7 +30,7 @@ namespace Artemis.Core.Plugins.Abstract
protected void ResolveAbsolutePath(Type type, object sender, ResolvePathEventArgs e)
{
if (sender.GetType().IsGenericType(type))
if (sender.GetType() == type || sender.GetType().IsGenericType(type))
{
// Start from the plugin directory
if (e.RelativePart != null && e.FileName != null)

View File

@ -0,0 +1,74 @@
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Surface;
using Artemis.Core.Services.Interfaces;
using SkiaSharp;
namespace Artemis.Core.Plugins.LayerBrush.Abstract
{
public abstract class PerLedLayerBrush<T> : PropertiesLayerBrush<T> where T : LayerPropertyGroup
{
protected PerLedLayerBrush()
{
BrushType = LayerBrushType.Regular;
}
/// <summary>
/// The main method of rendering for this type of brush. Called once per frame for each LED in the layer
/// <para>
/// Note: Due to transformations, the render point may not match the position of the LED, always use the render
/// point to determine where the color will go.
/// </para>
/// </summary>
/// <param name="led">The LED that will receive the color</param>
/// <param name="renderPoint">The point at which the color is located</param>
/// <returns>The color the LED will receive</returns>
public abstract SKColor GetColor(ArtemisLed led, SKPoint renderPoint);
internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
{
// This lil' snippet renders per LED, it's neater but doesn't support translations
Layer.ExcludeCanvasFromTranslation(canvas, true);
var shapePath = new SKPath(Layer.LayerShape.Path);
Layer.IncludePathInTranslation(shapePath, true);
canvas.ClipPath(shapePath);
using var pointsPath = new SKPath();
using var ledPaint = new SKPaint();
foreach (var artemisLed in Layer.Leds)
{
pointsPath.AddPoly(new[]
{
new SKPoint(0, 0),
new SKPoint(artemisLed.AbsoluteRenderRectangle.Left - Layer.Bounds.Left, artemisLed.AbsoluteRenderRectangle.Top - Layer.Bounds.Top)
});
}
Layer.ExcludePathFromTranslation(pointsPath, true);
var points = pointsPath.Points;
for (var index = 0; index < Layer.Leds.Count; index++)
{
var artemisLed = Layer.Leds[index];
var renderPoint = points[index * 2 + 1];
// Let the brush determine the color
ledPaint.Color = GetColor(artemisLed, renderPoint);
var ledRectangle = SKRect.Create(
artemisLed.AbsoluteRenderRectangle.Left - Layer.Bounds.Left,
artemisLed.AbsoluteRenderRectangle.Top - Layer.Bounds.Top,
artemisLed.AbsoluteRenderRectangle.Width,
artemisLed.AbsoluteRenderRectangle.Height
);
canvas.DrawRect(ledRectangle, ledPaint);
}
}
internal override void Initialize(IRenderElementService renderElementService)
{
InitializeProperties(renderElementService);
}
}
}

View File

@ -65,13 +65,28 @@ namespace Artemis.UI.Shared.Controls
_timer.Stop();
}
public static Size ResizeKeepAspect(Size src, double maxWidth, double maxHeight)
{
double scale;
if (maxWidth == double.PositiveInfinity && maxHeight != double.PositiveInfinity)
scale = maxHeight / src.Height;
else if (maxWidth != double.PositiveInfinity && maxHeight == double.PositiveInfinity)
scale = maxWidth / src.Width;
else if (maxWidth == double.PositiveInfinity && maxHeight == double.PositiveInfinity)
return src;
scale = Math.Min(maxWidth / src.Width, maxHeight / src.Height);
return new Size(src.Width * scale, src.Height * scale);
}
protected override void OnRender(DrawingContext drawingContext)
{
if (Device == null)
return;
// Determine the scale required to fit the desired size of the control
var measureSize = MeasureOverride(Size.Empty);
var measureSize = MeasureDevice();
var scale = Math.Min(DesiredSize.Width / measureSize.Width, DesiredSize.Height / measureSize.Height);
var scaledRect = new Rect(0, 0, measureSize.Width * scale, measureSize.Height * scale);
@ -103,7 +118,7 @@ namespace Artemis.UI.Shared.Controls
drawingContext.DrawDrawing(_backingStore);
}
protected override Size MeasureOverride(Size availableSize)
private Size MeasureDevice()
{
if (Device == null)
return Size.Empty;
@ -111,10 +126,18 @@ namespace Artemis.UI.Shared.Controls
var rotationRect = new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
rotationRect.Transform(new RotateTransform(Device.Rotation).Value);
// TODO: If availableSize exceeds what we need, scale up
return rotationRect.Size;
}
protected override Size MeasureOverride(Size availableSize)
{
if (Device == null)
return Size.Empty;
var deviceSize = MeasureDevice();
return ResizeKeepAspect(deviceSize, availableSize.Width, availableSize.Height);
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
_timer.Stop();

View File

@ -88,7 +88,7 @@ namespace Artemis.UI.Shared.Controls
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
CreateCustomGeometry(2.0);
else
CreateCustomGeometry(0);
CreateCustomGeometry(1.0);
break;
case Shape.Rectangle:
if (Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || Led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.LayerProperties;
using Artemis.Core.Plugins.Abstract;

View File

@ -65,7 +65,7 @@ namespace Artemis.UI.Ninject.Factories
public interface IProfileLayerVmFactory : IVmFactory
{
ProfileLayerViewModel Create(Layer layer);
ProfileLayerViewModel Create(Layer layer, ProfileViewModel profileViewModel);
}
public interface IVisualizationToolVmFactory : IVmFactory

View File

@ -104,10 +104,11 @@
</Button.ContextMenu>
</Button>
<!-- Right side property if type is dynamic -->
<Button Grid.Row="0"
<Grid Grid.Row="0"
Grid.Column="3"
Background="{DynamicResource PrimaryHueMidBrush}"
Visibility="{Binding SelectedOperator.SupportsRightSide, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
<!-- Right side property if type is dynamic -->
<Button Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"
Style="{StaticResource DisplayConditionButton}"
ToolTip="{Binding SelectedRightSideProperty.DisplayPropertyPath}"
@ -137,10 +138,11 @@
</Grid>
</Button>
<Grid Grid.Row="0"
Grid.Column="3"
<!-- Right side property if type is static -->
<materialDesign:Transitioner SelectedIndex="{Binding RightSideTransitionIndex}"
DefaultTransitionOrigin="0.5, 0.5"
Margin="3 -4"
Visibility="{Binding ShowRightSidePropertySelection, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
<materialDesign:Transitioner SelectedIndex="{Binding RightSideTransitionIndex}" DefaultTransitionOrigin="0.5, 0.5" Margin="3 -4">
<Button Style="{StaticResource DisplayConditionButton}"
Background="{DynamicResource PrimaryHueMidBrush}"
BorderBrush="{DynamicResource PrimaryHueMidBrush}"

View File

@ -103,7 +103,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
public Layer SelectedLayer => SelectedProfileElement as Layer;
public Folder SelectedFolder => SelectedProfileElement as Folder;
public BindableCollection<LayerPropertyGroupViewModel> LayerPropertyGroups
{
get => _layerPropertyGroups;

View File

@ -197,7 +197,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
LoadWorkspaceSettings();
_profileEditorService.StopRegularRender();
Module.ActiveProfileChanged += ModuleOnActiveProfileChanged;
Task.Run(LoadProfiles);
Execute.PostToUIThread(LoadProfiles);
base.OnInitialActivate();
}
@ -262,8 +262,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
profiles.Add(activeProfile);
// Populate the UI collection
Execute.PostToUIThread(() =>
{
Profiles.AddRange(profiles.Except(Profiles).ToList());
Profiles.RemoveRange(Profiles.Except(profiles).ToList());
var index = 0;
@ -278,7 +276,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor
_profileEditorService.ChangeSelectedProfile(activeProfile);
if (!activeProfile.IsActivated)
_profileService.ActivateProfile(Module, activeProfile);
});
}
}
}

View File

@ -16,8 +16,7 @@
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="Gray" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetProperty="(Path.StrokeThickness)" To="0.5" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="#99808080" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
@ -25,7 +24,6 @@
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="{StaticResource Accent700}" Duration="0:0:0.5" />
<DoubleAnimation Storyboard.TargetProperty="(Path.StrokeThickness)" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
@ -38,14 +36,14 @@
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.Opacity)" To="0" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="#A7A7A7" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(Path.Opacity)" To="1" Duration="0:0:0.5" />
<ColorAnimation Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="#00A7A7A7" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
@ -54,21 +52,10 @@
</Style>
</UserControl.Resources>
<Canvas>
<!-- The part of the layer's shape that falls outside the layer -->
<Path Data="{Binding ShapeGeometry, Mode=OneWay}"
StrokeThickness="0.5"
Style="{StaticResource SelectedShapeStyle}">
<Path Data="{Binding ShapeGeometry, Mode=OneWay}" StrokeThickness="{Binding StrokeThickness}" Style="{StaticResource SelectedShapeStyle}">
<Path.Stroke>
<SolidColorBrush Color="{StaticResource Primary700}" />
</Path.Stroke>
</Path>
<Path Data="{Binding LayerGeometry, Mode=OneWay}"
ClipToBounds="False"
Stroke="#A7A7A7"
StrokeThickness="1"
StrokeLineJoin="Round"
x:Name="LayerPath"
Style="{StaticResource SelectedLayerStyle}" />
</Canvas>
</UserControl>

View File

@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@ -10,7 +11,6 @@ using Artemis.Core.Models.Surface;
using Artemis.UI.Extensions;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services.Interfaces;
using RGB.NET.Core;
using Stylet;
using Rectangle = Artemis.Core.Models.Profile.LayerShapes.Rectangle;
@ -20,52 +20,35 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
{
private readonly ILayerEditorService _layerEditorService;
private readonly IProfileEditorService _profileEditorService;
private Geometry _layerGeometry;
private Geometry _opacityGeometry;
private Geometry _shapeGeometry;
private RenderTargetBitmap _layerGeometryBitmap;
private Rect _viewportRectangle;
private readonly ProfileViewModel _profileViewModel;
private bool _isSelected;
private Geometry _shapeGeometry;
private Rect _viewportRectangle;
public ProfileLayerViewModel(Layer layer, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService)
public ProfileLayerViewModel(Layer layer, ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService)
{
_profileViewModel = profileViewModel;
_profileEditorService = profileEditorService;
_layerEditorService = layerEditorService;
Layer = layer;
Update();
Layer.RenderPropertiesUpdated += LayerOnRenderPropertiesUpdated;
_profileEditorService.ProfileElementSelected += OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementUpdated;
_profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated;
_profileViewModel.PanZoomViewModel.PropertyChanged += PanZoomViewModelOnPropertyChanged;
}
public Layer Layer { get; }
public Geometry LayerGeometry
{
get => _layerGeometry;
set => SetAndNotify(ref _layerGeometry, value);
}
public Geometry OpacityGeometry
{
get => _opacityGeometry;
set => SetAndNotify(ref _opacityGeometry, value);
}
public Geometry ShapeGeometry
{
get => _shapeGeometry;
set => SetAndNotify(ref _shapeGeometry, value);
}
public RenderTargetBitmap LayerGeometryBitmap
{
get => _layerGeometryBitmap;
set => SetAndNotify(ref _layerGeometryBitmap, value);
}
public Rect ViewportRectangle
{
get => _viewportRectangle;
@ -76,12 +59,36 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}
}
public double StrokeThickness
{
get
{
if (IsSelected)
return Math.Max(2 / _profileViewModel.PanZoomViewModel.Zoom, 1);
return Math.Max(2 / _profileViewModel.PanZoomViewModel.Zoom, 1) / 2;
}
}
public double LayerStrokeThickness
{
get
{
if (IsSelected)
return StrokeThickness / 2;
return StrokeThickness;
}
}
public Thickness LayerPosition => new Thickness(ViewportRectangle.Left, ViewportRectangle.Top, 0, 0);
public bool IsSelected
{
get => _isSelected;
set => SetAndNotify(ref _isSelected, value);
set
{
if (!SetAndNotify(ref _isSelected, value)) return;
NotifyOfPropertyChange(nameof(StrokeThickness));
}
}
protected override void Dispose(bool disposing)
@ -92,84 +99,29 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
_profileEditorService.ProfileElementSelected -= OnProfileElementSelected;
_profileEditorService.SelectedProfileElementUpdated -= OnSelectedProfileElementUpdated;
_profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated;
_profileViewModel.PanZoomViewModel.PropertyChanged -= PanZoomViewModelOnPropertyChanged;
}
base.Dispose(disposing);
}
private void PanZoomViewModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(_profileViewModel.PanZoomViewModel.Zoom))
{
NotifyOfPropertyChange(nameof(StrokeThickness));
NotifyOfPropertyChange(nameof(LayerStrokeThickness));
}
}
private void Update()
{
IsSelected = _profileEditorService.SelectedProfileElement == Layer;
CreateLayerGeometry();
CreateShapeGeometry();
CreateViewportRectangle();
}
private void CreateLayerGeometry()
{
Execute.OnUIThread(() =>
{
if (!Layer.Leds.Any())
{
LayerGeometry = Geometry.Empty;
OpacityGeometry = Geometry.Empty;
ViewportRectangle = Rect.Empty;
return;
}
var group = new GeometryGroup();
group.FillRule = FillRule.Nonzero;
foreach (var led in Layer.Leds)
{
Geometry geometry;
switch (led.RgbLed.Shape)
{
case Shape.Custom:
if (led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keyboard || led.RgbLed.Device.DeviceInfo.DeviceType == RGBDeviceType.Keypad)
geometry = CreateCustomGeometry(led, 2);
else
geometry = CreateCustomGeometry(led, 1);
break;
case Shape.Rectangle:
geometry = CreateRectangleGeometry(led);
break;
case Shape.Circle:
geometry = CreateCircleGeometry(led);
break;
default:
throw new ArgumentOutOfRangeException();
}
group.Children.Add(geometry);
}
var layerGeometry = group.GetOutlinedPathGeometry();
var opacityGeometry = Geometry.Combine(Geometry.Empty, layerGeometry, GeometryCombineMode.Exclude, new TranslateTransform());
LayerGeometry = layerGeometry;
OpacityGeometry = opacityGeometry;
// Render the store as a bitmap
var drawingImage = new DrawingImage(new GeometryDrawing(new SolidColorBrush(Colors.Black), null, LayerGeometry));
var image = new Image {Source = drawingImage};
var bitmap = new RenderTargetBitmap(
(int) (LayerGeometry.Bounds.Width * 2.5),
(int) (LayerGeometry.Bounds.Height * 2.5),
96,
96,
PixelFormats.Pbgra32
);
image.Arrange(new Rect(0, 0, bitmap.Width, bitmap.Height));
bitmap.Render(image);
bitmap.Freeze();
LayerGeometryBitmap = bitmap;
});
}
private void CreateShapeGeometry()
{
if (Layer.LayerShape == null || !Layer.Leds.Any())
@ -213,21 +165,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private Geometry CreateRectangleGeometry(ArtemisLed led)
{
var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
rect.Inflate(1, 1);
return new RectangleGeometry(rect);
}
private Geometry CreateCircleGeometry(ArtemisLed led)
{
var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
rect.Inflate(1, 1);
return new EllipseGeometry(rect);
}
private Geometry CreateCustomGeometry(ArtemisLed led, double deflateAmount)
{
var rect = led.RgbLed.AbsoluteLedRectangle.ToWindowsRect(1);
rect.Inflate(1, 1);
try
{
var geometry = Geometry.Combine(

View File

@ -217,8 +217,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
}
private void ApplyActiveProfile()
{
Execute.PostToUIThread(() =>
{
var layerViewModels = CanvasViewModels.Where(vm => vm is ProfileLayerViewModel).Cast<ProfileLayerViewModel>().ToList();
var layers = _profileEditorService.SelectedProfile?.GetAllLayers() ?? new List<Layer>();
@ -227,7 +225,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
foreach (var layer in layers)
{
if (layerViewModels.All(vm => vm.Layer != layer))
CanvasViewModels.Add(_profileLayerVmFactory.Create(layer));
CanvasViewModels.Add(_profileLayerVmFactory.Create(layer, this));
}
// Remove layers that no longer exist
@ -237,7 +235,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
profileLayerViewModel.Dispose();
CanvasViewModels.Remove(profileLayerViewModel);
}
});
}
protected override void OnActivate()
{
ApplyActiveProfile();
}
private void ApplySurfaceConfiguration(ArtemisSurface surface)

View File

@ -59,8 +59,7 @@
Please note that having this window open can have a performance impact on your system.
</TextBlock>
<controls:DeviceVisualizer Grid.Row="1" Device="{Binding Device}" HighlightedLeds="{Binding SelectedLeds}" HorizontalAlignment="Center"
Height="320" Width="800" ShowColors="True" />
<controls:DeviceVisualizer Grid.Row="1" Device="{Binding Device}" HighlightedLeds="{Binding SelectedLeds}" HorizontalAlignment="Center" MaxHeight="500" ShowColors="True" />
<Expander Grid.Row="2" Width="800" VerticalAlignment="Center" Header="Device properties">
<StackPanel Orientation="Horizontal">

View File

@ -12,6 +12,14 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{
Icon = icon;
ActiveItem = configurationViewModel;
ActiveItem.Closed += ActiveItemOnClosed;
}
private void ActiveItemOnClosed(object? sender, CloseEventArgs e)
{
ActiveItem.Closed -= ActiveItemOnClosed;
RequestClose();
}
}
}

View File

@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.UI", "Artemis.UI\Ar
ProjectSection(ProjectDependencies) = postProject
{07678400-2FE1-4C6E-A8D4-4F9F3C0630EA} = {07678400-2FE1-4C6E-A8D4-4F9F3C0630EA}
{AB80F106-5444-46AA-A255-F765DD2F04F1} = {AB80F106-5444-46AA-A255-F765DD2F04F1}
{3D83760B-0A36-4C8F-978D-7949C3FC862B} = {3D83760B-0A36-4C8F-978D-7949C3FC862B}
{8DC7960F-6DDF-4007-A155-17E124F39374} = {8DC7960F-6DDF-4007-A155-17E124F39374}
{DCF7C321-95DC-4507-BB61-A7C5356E58EC} = {DCF7C321-95DC-4507-BB61-A7C5356E58EC}
{E592F239-FAA0-4840-9C85-46E5867D06D5} = {E592F239-FAA0-4840-9C85-46E5867D06D5}
@ -75,6 +76,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.Plugins.LayerEffect
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataModelExpansions", "DataModelExpansions", "{4E85F6B5-83FB-4830-8787-555103F26ECD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Artemis.Plugins.Devices.Debug", "Plugins\Artemis.Plugins.Devices.Debug\Artemis.Plugins.Devices.Debug.csproj", "{3D83760B-0A36-4C8F-978D-7949C3FC862B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -251,6 +254,14 @@ Global
{62214042-667E-4B29-B64E-1A68CE6FE209}.Release|Any CPU.Build.0 = Release|Any CPU
{62214042-667E-4B29-B64E-1A68CE6FE209}.Release|x64.ActiveCfg = Release|Any CPU
{62214042-667E-4B29-B64E-1A68CE6FE209}.Release|x64.Build.0 = Release|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Debug|x64.Build.0 = Debug|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Release|Any CPU.Build.0 = Release|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Release|x64.ActiveCfg = Release|Any CPU
{3D83760B-0A36-4C8F-978D-7949C3FC862B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -278,6 +289,7 @@ Global
{2C1477DC-7A5C-4B65-85DB-1F16A18FB2EC} = {E830A02B-A7E5-4A6B-943F-76B0A542630C}
{62214042-667E-4B29-B64E-1A68CE6FE209} = {2C1477DC-7A5C-4B65-85DB-1F16A18FB2EC}
{4E85F6B5-83FB-4830-8787-555103F26ECD} = {E830A02B-A7E5-4A6B-943F-76B0A542630C}
{3D83760B-0A36-4C8F-978D-7949C3FC862B} = {88792A7E-F037-4280-81D3-B131508EF1D8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C203080A-4473-4CC2-844B-F552EA43D66A}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

View File

@ -0,0 +1,76 @@
<?xml version="1.0"?>
<Device xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Asus Maximus X Hero</Name>
<Description>Asus Maximus X Hero</Description>
<Type>None</Type>
<Lighting>Device</Lighting>
<Vendor>Asus</Vendor>
<Model>Maximus X Hero</Model>
<Width>555</Width>
<Height>690</Height>
<LedUnitWidth>150</LedUnitWidth>
<LedUnitHeight>150</LedUnitHeight>
<ImageBasePath>Images\Asus\Mainboards</ImageBasePath>
<DeviceImage>MAXIMUS-X-HERO.png</DeviceImage>
<Leds>
<Led Id="Logo">
<Shape> M0.241,0.804 L0.276,0.871 L0.285,0.861 L0.264,0.824 L0.273,0.812 L0.269,0.797 L0.273,0.788 L0.29,0.818 L0.295,0.802 L0.258,0.734 L0.264,0.796z M0.312,0.736 L0.272,0.668 L0.258,0.718 L0.298,0.798 L0.301,0.778 L0.283,0.747 L0.293,0.723 L0.307,0.748z M0.328,0.66 L0.303,0.648 L0.288,0.589 L0.287,0.613 L0.294,0.641 L0.278,0.636 L0.275,0.652 L0.3,0.668 L0.313,0.723 L0.318,0.709 L0.31,0.673 L0.317,0.672 L0.324,0.679z M0.296,0.564 L0.292,0.584 L0.33,0.651 L0.334,0.633z M0.318,0.494 L0.321,0.545 L0.3,0.553 L0.336,0.623 L0.339,0.607 L0.319,0.57 L0.331,0.563 L0.327,0.534 L0.347,0.569 L0.351,0.555z M0.331,0.416 L0.367,0.481 L0.358,0.535 L0.351,0.542 L0.315,0.472 L0.318,0.462 L0.354,0.521 L0.362,0.493 L0.327,0.427z M0.347,0.349 L0.334,0.394 L0.334,0.405 L0.354,0.446 L0.366,0.401 L0.378,0.42 L0.367,0.466 L0.372,0.467 L0.383,0.423 L0.382,0.409 L0.364,0.376 L0.351,0.419 L0.34,0.397 L0.347,0.364z M0.398,0.354 L0.4,0.322 L0.395,0.307 L0.409,0.311 L0.415,0.296 L0.39,0.282 L0.381,0.249 L0.374,0.237 L0.378,0.275 L0.363,0.27 L0.361,0.289 L0.385,0.301 L0.387,0.303z </Shape>
<X>16.6</X>
<Y>94</Y>
<Height>1</Height>
</Led>
<Led Id="Mainboard1">
<Shape> M0.56,0.404 L0.269,0.854 L0.283,0.857 L0.573,0.407z</Shape>
<X>7.5</X>
<Y>44</Y>
<Height>2</Height>
</Led>
<Led Id="Mainboard2">
<Shape> M0.166,0.341 L0.144,0.459 L0.145,0.657 L0.712,0.657 L0.809,0.502 L0.81,0.391 L0.775,0.337 L0.167,0.334z</Shape>
<X>133.8</X>
<Y>273.6</Y>
<Width>2</Width>
</Led>
<Led Id="Mainboard3">
<Shape> M0.305,0.581 L0.321,0.621 L0.342,0.632 L0.369,0.642 L0.372,0.635 L0.324,0.594z M0.358,0.595 L0.398,0.658 L0.418,0.675 L0.435,0.683 L0.436,0.679 L0.409,0.645 L0.423,0.629 L0.438,0.614 L0.461,0.597 L0.502,0.567 L0.552,0.534 L0.573,0.521 L0.61,0.504 L0.647,0.491 L0.648,0.486 L0.615,0.487 L0.608,0.49 L0.577,0.492 L0.564,0.498 L0.552,0.498 L0.544,0.503 L0.535,0.505 L0.52,0.514 L0.493,0.535 L0.415,0.61 L0.409,0.612 L0.361,0.592z M0.517,0.624 L0.65,0.559 L0.656,0.562 L0.639,0.6 L0.619,0.634 L0.607,0.651 L0.584,0.674 L0.564,0.684 L0.525,0.682 L0.488,0.671 L0.448,0.658 L0.429,0.647 L0.433,0.64 L0.53,0.576 L0.585,0.547 L0.617,0.534 L0.658,0.523 L0.67,0.522 L0.67,0.53 L0.667,0.535 L0.603,0.565 L0.526,0.601 L0.458,0.643 L0.49,0.654 L0.53,0.662 L0.543,0.662 L0.571,0.652 L0.604,0.597z</Shape>
<X>334.3</X>
<Y>423.4</Y>
</Led>
</Leds>
<LedImageLayouts>
<LedImageLayout>
<LedImages />
</LedImageLayout>
<LedImageLayout>
<LedImages />
</LedImageLayout>
<LedImageLayout>
<LedImages />
</LedImageLayout>
<LedImageLayout>
<LedImages>
<LedImage Id="Logo" />
</LedImages>
</LedImageLayout>
<LedImageLayout>
<LedImages>
<LedImage Id="Logo" />
</LedImages>
</LedImageLayout>
<LedImageLayout>
<LedImages>
<LedImage Id="Mainboard2" />
</LedImages>
</LedImageLayout>
<LedImageLayout>
<LedImages>
<LedImage Id="Mainboard3" />
</LedImages>
</LedImageLayout>
<LedImageLayout>
<LedImages>
<LedImage Id="Mainboard1" />
</LedImages>
</LedImageLayout>
</LedImageLayouts>
</Device>

View File

@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyTitle>Artemis.Plugins.Devices.Wooting</AssemblyTitle>
<Product>Artemis.Plugins.Devices.Wooting</Product>
<Copyright>Copyright © 2019</Copyright>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.3</LangVersion>
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<UseWPF>true</UseWPF>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<None Remove="Views\DebugConfigurationView.xaml" />
</ItemGroup>
<ItemGroup>
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MaterialDesignThemes" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Artemis.Core\Artemis.Core.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="RGB.NET.Core">
<HintPath>..\..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Core.dll</HintPath>
</Reference>
<Reference Include="RGB.NET.Devices.Debug">
<HintPath>..\..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Devices.Debug.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<None Update="Images\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Layouts\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<Exec Command="echo Copying resources to plugin output directory&#xD;&#xA;XCOPY &quot;$(ProjectDir)Images&quot; &quot;$(TargetDir)Images&quot; /s /q /i /y&#xD;&#xA;XCOPY &quot;$(ProjectDir)Layouts&quot; &quot;$(TargetDir)Layouts&quot; /s /q /i /y&#xD;&#xA;echo Copying plugin to Artemis.UI output directory&#xD;&#xA;XCOPY &quot;$(TargetDir.TrimEnd('\'))&quot; &quot;$(SolutionDir)\Artemis.UI\$(OutDir)Plugins\$(ProjectName)&quot; /s /q /i /y" />
</Target>
</Project>

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Abstract.ViewModels;
using Artemis.Core.Plugins.Models;
using Artemis.Core.Services.Interfaces;
using Artemis.Plugins.Devices.Debug.Settings;
using Artemis.Plugins.Devices.Debug.ViewModels;
using RGB.NET.Core;
using RGB.NET.Devices.Debug;
namespace Artemis.Plugins.Devices.Debug
{
// ReSharper disable once UnusedMember.Global
public class DebugDeviceProvider : DeviceProvider
{
private readonly IRgbService _rgbService;
private readonly PluginSettings _settings;
public DebugDeviceProvider(IRgbService rgbService, PluginSettings settings) : base(RGB.NET.Devices.Debug.DebugDeviceProvider.Instance)
{
_settings = settings;
_rgbService = rgbService;
}
public override void EnablePlugin()
{
HasConfigurationViewModel = true;
PathHelper.ResolvingAbsolutePath += PathHelperOnResolvingAbsolutePath;
var definitions = _settings.GetSetting<List<DeviceDefinition>>("DeviceDefinitions");
if (definitions.Value == null)
definitions.Value = new List<DeviceDefinition>();
foreach (var deviceDefinition in definitions.Value)
RGB.NET.Devices.Debug.DebugDeviceProvider.Instance.AddFakeDeviceDefinition(deviceDefinition.Layout, deviceDefinition.ImageLayout);
_rgbService.AddDeviceProvider(RgbDeviceProvider);
}
private void PathHelperOnResolvingAbsolutePath(object sender, ResolvePathEventArgs e)
{
if (sender is DebugRGBDevice debugRgbDevice)
{
if (debugRgbDevice.LayoutPath.Contains("\\Layouts\\"))
{
var rootDirectory = debugRgbDevice.LayoutPath.Split("\\Layouts")[0];
e.FinalPath = Path.Combine(rootDirectory, e.RelativePath);
}
var test =debugRgbDevice.LayoutPath;
}
}
public override void DisablePlugin()
{
// TODO: Remove the device provider from the surface
}
public override PluginConfigurationViewModel GetConfigurationViewModel()
{
return new DebugConfigurationViewModel(this, _settings);
}
}
}

View File

@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c6bdb6d9-062d-4c28-a280-f3bd6197f07f")]

View File

@ -0,0 +1,8 @@
namespace Artemis.Plugins.Devices.Debug.Settings
{
public class DeviceDefinition
{
public string Layout { get; set; }
public string ImageLayout { get; set; }
}
}

View File

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Abstract.ViewModels;
using Artemis.Core.Plugins.Models;
using Artemis.Plugins.Devices.Debug.Settings;
using Stylet;
namespace Artemis.Plugins.Devices.Debug.ViewModels
{
public class DebugConfigurationViewModel : PluginConfigurationViewModel
{
private PluginSetting<List<DeviceDefinition>> _definitions;
public DebugConfigurationViewModel(Plugin plugin, PluginSettings settings) : base(plugin)
{
_definitions = settings.GetSetting<List<DeviceDefinition>>("DeviceDefinitions");
Definitions = new BindableCollection<DeviceDefinition>(_definitions.Value);
}
public BindableCollection<DeviceDefinition> Definitions { get; }
public void SaveChanges()
{
// Ignore empty definitions
_definitions.Value.Clear();
_definitions.Value.AddRange(Definitions.Where(d => !string.IsNullOrWhiteSpace(d.Layout) || !string.IsNullOrWhiteSpace(d.ImageLayout)));
_definitions.Save();
RequestClose();
}
public void Cancel()
{
_definitions.RejectChanges();
RequestClose();
}
}
}

View File

@ -0,0 +1,49 @@
<UserControl x:Class="Artemis.Plugins.Devices.Debug.Views.DebugConfigurationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.Plugins.Devices.Debug.Views"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock>
Enter absolute paths to the device layout and choose the image layout you wish to test. <LineBreak/>
Please note that currently RGB.NET does not support changing devices on runtime and so any changes below won't be applied until you restart Artemis.
</TextBlock>
<DataGrid Grid.Row="1"
ItemsSource="{Binding Definitions}"
AutoGenerateColumns="False"
CanUserSortColumns="True"
CanUserAddRows="True"
materialDesign:DataGridAssist.CellPadding="13 8 8 8"
materialDesign:DataGridAssist.ColumnHeaderPadding="8"
HeadersVisibility="All">
<DataGrid.Columns>
<materialDesign:DataGridTextColumn Width="*"
Binding="{Binding Layout}"
Header="Layout path"
EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnEditingStyle}" />
<materialDesign:DataGridTextColumn Width="*"
Binding="{Binding ImageLayout}"
Header="Image layout (optional)"
EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnEditingStyle}" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0 0 5 0" Command="{s:Action Cancel}">
CANCEL
</Button>
<Button Style="{StaticResource MaterialDesignFlatMidBgButton}" Command="{s:Action SaveChanges}">
SAVE CHANGES
</Button>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.4.5.0" newVersion="1.4.5.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.5.0" newVersion="1.2.5.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Ninject" publicKeyToken="c7192dc5380945e7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.3.4.0" newVersion="3.3.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,7 @@
{
"Guid": "cad475d3-c621-4ec7-bbfc-784e3b4723ce",
"Name": "Debug Devices",
"Description": "Provides configurable debug devices to among other things easily test layouts",
"Version": "1.0.0.0",
"Main": "Artemis.Plugins.Devices.Debug.dll"
}

View File

@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using Artemis.Core.Extensions;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerBrush.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.Plugins.LayerBrushes.Noise.Utilities;
@ -8,7 +9,7 @@ using SkiaSharp;
namespace Artemis.Plugins.LayerBrushes.Noise
{
public class NoiseBrush : LayerBrush<NoiseBrushProperties>
public class NoiseBrush : PerLedLayerBrush<NoiseBrushProperties>
{
private static readonly Random Rand = new Random();
private readonly IRgbService _rgbService;
@ -60,75 +61,69 @@ namespace Artemis.Plugins.LayerBrushes.Noise
DetermineRenderScale();
}
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
{
var mainColor = Properties.MainColor.CurrentValue;
var secondColor = Properties.SecondaryColor.CurrentValue;
var gradientColor = Properties.GradientColor.CurrentValue;
var scale = Properties.Scale.CurrentValue;
var hardness = Properties.Hardness.CurrentValue / 100f;
// Scale down the render path to avoid computing a value for every pixel
var width = (int) Math.Floor(path.Bounds.Width * _renderScale);
var height = (int) Math.Floor(path.Bounds.Height * _renderScale);
CreateBitmap(width, height);
// Copy the layer path to use for the clip path
using var layerPath = new SKPath(Layer.Path);
// Undo any transformations
Layer.ExcludePathFromTranslation(layerPath);
// Apply the transformations so new ones may be applied, not sure if there is a better way to do this
using var clipPath = new SKPath(layerPath);
// Zero out the position of the clip path
clipPath.Transform(SKMatrix.MakeTranslation(Layer.Path.Bounds.Left * -1, Layer.Path.Bounds.Top * -1));
// Fill a canvas matching the final area that will be rendered
using var bitmapCanvas = new SKCanvas(_bitmap);
using var clipPaint = new SKPaint {Color = new SKColor(0, 0, 0, 255)};
bitmapCanvas.Clear();
bitmapCanvas.Scale(_renderScale);
bitmapCanvas.DrawPath(clipPath, clipPaint);
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var scrolledX = x + _x;
var scrolledY = y + _y;
var evalX = 0.1 * scale.Width * scrolledX / width;
var evalY = 0.1 * scale.Height * scrolledY / height;
if (double.IsInfinity(evalX) || double.IsNaN(evalX) || double.IsNaN(evalY) || double.IsInfinity(evalY))
continue;
var pixel = _bitmap.GetPixel(x, y);
if (pixel.Alpha != 255)
continue;
var v = (float) _noise.Evaluate(evalX, evalY, _z) * hardness;
var amount = Math.Max(0f, Math.Min(1f, v));
if (Properties.ColorType.BaseValue == ColorMappingType.Simple)
_bitmap.SetPixel(x, y, mainColor.Interpolate(secondColor, amount));
else if (gradientColor != null && _colorMap.Length == 101)
{
var color = _colorMap[(int) Math.Round(amount * 100, MidpointRounding.AwayFromZero)];
_bitmap.SetPixel(x, y, color);
}
}
}
var bitmapTransform = SKMatrix.Concat(
SKMatrix.MakeTranslation(path.Bounds.Left, path.Bounds.Top),
SKMatrix.MakeScale(1f / _renderScale, 1f / _renderScale)
);
if (Properties.ColorType.BaseValue == ColorMappingType.Simple)
paint.Color = Properties.SecondaryColor.CurrentValue;
using var foregroundShader = SKShader.CreateBitmap(_bitmap, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp, bitmapTransform);
paint.Shader = foregroundShader;
canvas.DrawPath(path, paint);
}
// public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
// {
// var mainColor = Properties.MainColor.CurrentValue;
// var secondColor = Properties.SecondaryColor.CurrentValue;
// var gradientColor = Properties.GradientColor.CurrentValue;
// var scale = Properties.Scale.CurrentValue;
// var hardness = Properties.Hardness.CurrentValue / 100f;
//
// // Scale down the render path to avoid computing a value for every pixel
// var width = (int) Math.Floor(path.Bounds.Width * _renderScale);
// var height = (int) Math.Floor(path.Bounds.Height * _renderScale);
//
// // This lil' snippet renders per LED, it's neater but doesn't support translations
// Layer.ExcludeCanvasFromTranslation(canvas);
//
// var shapePath = new SKPath(Layer.LayerShape.Path);
// Layer.IncludePathInTranslation(shapePath);
// canvas.ClipPath(shapePath);
//
// using var ledPaint = new SKPaint();
// foreach (var artemisLed in Layer.Leds)
// {
// var ledRectangle = SKRect.Create(
// artemisLed.AbsoluteRenderRectangle.Left - Layer.Bounds.Left,
// artemisLed.AbsoluteRenderRectangle.Top - Layer.Bounds.Top,
// artemisLed.AbsoluteRenderRectangle.Width,
// artemisLed.AbsoluteRenderRectangle.Height
// );
//
// var scrolledX = ledRectangle.MidX + _x;
// if (float.IsNaN(scrolledX))
// scrolledX = 0;
// var scrolledY = ledRectangle.MidY + _y;
// if (float.IsNaN(scrolledY))
// scrolledY = 0;
//
//
// var evalPath = new SKPath();
// evalPath.AddPoly(new[] {new SKPoint(0, 0), new SKPoint(scrolledX, scrolledY)});
// Layer.ExcludePathFromTranslation(evalPath);
//
// if (evalPath.Bounds.IsEmpty)
// continue;
//
// scrolledX = evalPath.Points[1].X;
// scrolledY = evalPath.Points[1].Y;
//
// var evalX = 0.1 * scale.Width * scrolledX / width;
// var evalY = 0.1 * scale.Height * scrolledY / height;
//
// var v = (float) _noise.Evaluate(evalX, evalY, _z) * hardness;
// var amount = Math.Max(0f, Math.Min(1f, v));
//
// if (Properties.ColorType.BaseValue == ColorMappingType.Simple)
// ledPaint.Color = mainColor.Interpolate(secondColor, amount);
// else if (gradientColor != null && _colorMap.Length == 101)
// ledPaint.Color = _colorMap[(int) Math.Round(amount * 100, MidpointRounding.AwayFromZero)];
// else
// ledPaint.Color = SKColor.Empty;
//
// canvas.DrawRect(ledRectangle, ledPaint);
// }
// }
private void GradientColorChanged(object sender, PropertyChangedEventArgs e)
{
@ -138,7 +133,7 @@ namespace Artemis.Plugins.LayerBrushes.Noise
private void DetermineRenderScale()
{
_renderScale = (float) (0.125f / _rgbService.RenderScale);
_renderScale = (float) (0.25f / _rgbService.RenderScale);
}
private void CreateBitmap(int width, int height)
@ -160,6 +155,36 @@ namespace Artemis.Plugins.LayerBrushes.Noise
_colorMap = colorMap;
}
public override SKColor GetColor(ArtemisLed led, SKPoint renderPoint)
{
var mainColor = Properties.MainColor.CurrentValue;
var secondColor = Properties.SecondaryColor.CurrentValue;
var gradientColor = Properties.GradientColor.CurrentValue;
var scale = Properties.Scale.CurrentValue;
var hardness = Properties.Hardness.CurrentValue / 100f;
var scrolledX = renderPoint.X + _x;
if (float.IsNaN(scrolledX))
scrolledX = 0;
var scrolledY = renderPoint.Y + _y;
if (float.IsNaN(scrolledY))
scrolledY = 0;
var evalX = scrolledX * (scale.Width *-1) / 1000f;
var evalY = scrolledY * (scale.Height*-1) / 1000f;
var v = (float) _noise.Evaluate(evalX, evalY, _z) * hardness;
var amount = Math.Max(0f, Math.Min(1f, v));
if (Properties.ColorType.BaseValue == ColorMappingType.Simple)
return mainColor.Interpolate(secondColor, amount);
else if (gradientColor != null && _colorMap.Length == 101)
return _colorMap[(int) Math.Round(amount * 100, MidpointRounding.AwayFromZero)];
else
return SKColor.Empty;
}
}
public enum ColorMappingType