diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs index 370a72196..315ecac2d 100644 --- a/src/Artemis.Core/Models/Profile/Folder.cs +++ b/src/Artemis.Core/Models/Profile/Folder.cs @@ -70,12 +70,12 @@ namespace Artemis.Core.Models.Profile // Update the layer timeline, this will give us a new delta time which could be negative in case the main segment wrapped back // to it's start - var timelineDeltaTime = UpdateTimeline(deltaTime); + UpdateTimeline(deltaTime); foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { baseLayerEffect.BaseProperties?.Update(); - baseLayerEffect.Update(timelineDeltaTime); + baseLayerEffect.Update(deltaTime); } // Iterate the children in reverse because that's how they must be rendered too diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index f5be0698f..99265b4bc 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -70,7 +70,6 @@ namespace Artemis.Core.Models.Profile General.PropertyGroupInitialized += GeneralOnPropertyGroupInitialized; ApplyRenderElementEntity(); - ApplyRenderElementDefaults(); } internal LayerEntity LayerEntity { get; set; } @@ -224,17 +223,17 @@ namespace Artemis.Core.Models.Profile // Update the layer timeline, this will give us a new delta time which could be negative in case the main segment wrapped back // to it's start - var timelineDeltaTime = UpdateTimeline(deltaTime); + UpdateTimeline(deltaTime); General.Update(); Transform.Update(); LayerBrush.BaseProperties?.Update(); - LayerBrush.Update(timelineDeltaTime); + LayerBrush.Update(deltaTime); foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { baseLayerEffect.BaseProperties?.Update(); - baseLayerEffect.Update(timelineDeltaTime); + baseLayerEffect.Update(deltaTime); } } @@ -256,7 +255,10 @@ namespace Artemis.Core.Models.Profile else { var progress = timeOverride.TotalMilliseconds % MainSegmentLength.TotalMilliseconds; - TimelinePosition = TimeSpan.FromMilliseconds(progress) + StartSegmentLength; + if (progress > 0) + TimelinePosition = TimeSpan.FromMilliseconds(progress) + StartSegmentLength; + else + TimelinePosition = StartSegmentLength; } } else @@ -437,6 +439,39 @@ namespace Artemis.Core.Models.Profile return position; } + /// + /// Excludes the provided path from the translations applied to the layer by applying translations that cancel the + /// layer translations out + /// + /// + public void ExcludePathFromTranslation(SKPath path) + { + var sizeProperty = Transform.Scale.CurrentValue; + var rotationProperty = Transform.Rotation.CurrentValue; + + var anchorPosition = GetLayerAnchorPosition(Path); + 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 reversedXScale = 1f / (sizeProperty.Width / 100f); + var reversedYScale = 1f / (sizeProperty.Height / 100f); + + if (General.FillType == LayerFillType.Stretch) + { + path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y)); + path.Transform(SKMatrix.MakeScale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y)); + path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1)); + } + else + { + path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y)); + path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1)); + } + } + #endregion #region LED management diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index 4156086be..dd8d7ba26 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -17,8 +17,7 @@ namespace Artemis.Core.Models.Profile { protected void ApplyRenderElementDefaults() { - if (MainSegmentLength <= TimeSpan.Zero) - MainSegmentLength = TimeSpan.FromSeconds(5); + MainSegmentLength = TimeSpan.FromSeconds(5); } protected void ApplyRenderElementEntity() diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml index 87a3ed132..f4b699c5d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml @@ -172,130 +172,62 @@ - - - - - - - + + + + + - - - - + + + - + - - - - - - - - - Start - - - - - - - - - - - - - Main - - - - - - - - - - - - - - - - - End - - - - - - - - - - - - - - + - + - - + + - + @@ -303,8 +235,8 @@ @@ -409,15 +341,15 @@ + IsEnabled="{Binding Data.StartTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" /> + IsEnabled="{Binding Data.MainTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" /> + IsEnabled="{Binding Data.EndTimelineSegmentViewModel.SegmentEnabled, Converter={StaticResource InverseBooleanConverter}, Source={StaticResource DataContextProxy}}" /> diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 62e2df39e..c708e5fd1 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -35,6 +35,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties private RenderProfileElement _selectedProfileElement; private TimelineViewModel _timelineViewModel; private TreeViewModel _treeViewModel; + private TimelineSegmentViewModel _startTimelineSegmentViewModel; + private TimelineSegmentViewModel _mainTimelineSegmentViewModel; + private TimelineSegmentViewModel _endTimelineSegmentViewModel; public LayerPropertiesViewModel(IProfileEditorService profileEditorService, ICoreService coreService, ISettingsService settingsService, ILayerPropertyVmFactory layerPropertyVmFactory) @@ -68,10 +71,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties public string FormattedCurrentTime => $"{Math.Floor(ProfileEditorService.CurrentTime.TotalSeconds):00}.{ProfileEditorService.CurrentTime.Milliseconds:000}"; - public Thickness TimeCaretPosition + public double TimeCaretPosition { - get => new Thickness(ProfileEditorService.CurrentTime.TotalSeconds * ProfileEditorService.PixelsPerSecond, 0, 0, 0); - set => ProfileEditorService.CurrentTime = TimeSpan.FromSeconds(value.Left / ProfileEditorService.PixelsPerSecond); + get => ProfileEditorService.CurrentTime.TotalSeconds * ProfileEditorService.PixelsPerSecond; + set => ProfileEditorService.CurrentTime = TimeSpan.FromSeconds(value / ProfileEditorService.PixelsPerSecond); } public int PropertyTreeIndex @@ -94,8 +97,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties if (!SetAndNotify(ref _selectedProfileElement, value)) return; NotifyOfPropertyChange(nameof(SelectedLayer)); NotifyOfPropertyChange(nameof(SelectedFolder)); - NotifyOfPropertyChange(nameof(StartSegmentEnabled)); - NotifyOfPropertyChange(nameof(EndSegmentEnabled)); } } @@ -127,6 +128,24 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties set => SetAndNotify(ref _timelineViewModel, value); } + public TimelineSegmentViewModel StartTimelineSegmentViewModel + { + get => _startTimelineSegmentViewModel; + set => SetAndNotify(ref _startTimelineSegmentViewModel, value); + } + + public TimelineSegmentViewModel MainTimelineSegmentViewModel + { + get => _mainTimelineSegmentViewModel; + set => SetAndNotify(ref _mainTimelineSegmentViewModel, value); + } + + public TimelineSegmentViewModel EndTimelineSegmentViewModel + { + get => _endTimelineSegmentViewModel; + set => SetAndNotify(ref _endTimelineSegmentViewModel, value); + } + protected override void OnInitialActivate() { PopulateProperties(ProfileEditorService.SelectedProfileElement); @@ -227,11 +246,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties TimelineViewModel?.Dispose(); TimelineViewModel = _layerPropertyVmFactory.TimelineViewModel(this, LayerPropertyGroups); + StartTimelineSegmentViewModel?.Dispose(); + StartTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Start); + MainTimelineSegmentViewModel?.Dispose(); + MainTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Main); + EndTimelineSegmentViewModel?.Dispose(); + EndTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.End); ApplyLayerBrush(); ApplyEffects(); } + + private void SelectedLayerOnLayerBrushUpdated(object sender, EventArgs e) { ApplyLayerBrush(); @@ -564,163 +591,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties #endregion #region Segments - - public bool StartSegmentEnabled - { - get => SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero; - set - { - SelectedProfileElement.StartSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero; - ProfileEditorService.UpdateSelectedProfileElement(); - NotifyOfPropertyChange(nameof(StartSegmentEnabled)); - NotifyOfPropertyChange(nameof(CanAddSegment)); - } - } - public bool MainSegmentEnabled - { - get => SelectedProfileElement?.MainSegmentLength != TimeSpan.Zero; - set - { - SelectedProfileElement.MainSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero; - ProfileEditorService.UpdateSelectedProfileElement(); - NotifyOfPropertyChange(nameof(MainSegmentEnabled)); - NotifyOfPropertyChange(nameof(CanAddSegment)); - } - } - - public bool EndSegmentEnabled - { - get => SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero; - set - { - SelectedProfileElement.EndSegmentLength = value ? TimeSpan.FromSeconds(1) : TimeSpan.Zero; - ProfileEditorService.UpdateSelectedProfileElement(); - NotifyOfPropertyChange(nameof(EndSegmentEnabled)); - NotifyOfPropertyChange(nameof(CanAddSegment)); - } - } - - public bool CanAddSegment => !StartSegmentEnabled || !MainSegmentEnabled || !EndSegmentEnabled; - - public bool RepeatMainSegment - { - get => SelectedProfileElement?.RepeatMainSegment ?? false; - set - { - SelectedProfileElement.RepeatMainSegment = value; - ProfileEditorService.UpdateSelectedProfileElement(); - NotifyOfPropertyChange(nameof(RepeatMainSegment)); - } - } - - private bool _draggingStartSegment; - private bool _draggingMainSegment; - private bool _draggingEndSegment; - - public void DisableSegment(string segment) - { - if (segment == "Start") - StartSegmentEnabled = false; - else if (segment == "Main") - MainSegmentEnabled = false; - else if (segment == "End") - EndSegmentEnabled = false; - } - public void EnableSegment(string segment) { if (segment == "Start") - StartSegmentEnabled = true; + StartTimelineSegmentViewModel.EnableSegment(); else if (segment == "Main") - MainSegmentEnabled = true; + MainTimelineSegmentViewModel.EnableSegment(); else if (segment == "End") - EndSegmentEnabled = true; - } - - public void StartSegmentMouseDown(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).CaptureMouse(); - _draggingStartSegment = true; - } - - public void StartSegmentMouseUp(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).ReleaseMouseCapture(); - _draggingStartSegment = false; - } - - public void MainSegmentMouseDown(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).CaptureMouse(); - _draggingMainSegment = true; - } - - public void MainSegmentMouseUp(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).ReleaseMouseCapture(); - _draggingMainSegment = false; - } - - public void EndSegmentMouseDown(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).CaptureMouse(); - _draggingEndSegment = true; - } - - public void EndSegmentMouseUp(object sender, MouseButtonEventArgs e) - { - ((IInputElement) sender).ReleaseMouseCapture(); - _draggingEndSegment = false; - } - - public void SegmentMouseMove(object sender, MouseEventArgs e) - { - if (e.LeftButton == MouseButtonState.Pressed) - { - // Get the parent grid, need that for our position - var parent = (IInputElement) VisualTreeHelper.GetParent((DependencyObject) sender); - var x = Math.Max(0, e.GetPosition(parent).X); - var newTime = TimeSpan.FromSeconds(x / ProfileEditorService.PixelsPerSecond); - - // Round the time to something that fits the current zoom level - if (ProfileEditorService.PixelsPerSecond < 200) - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0); - else if (ProfileEditorService.PixelsPerSecond < 500) - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0); - else - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds)); - - // If holding down shift, snap to the closest element on the timeline - if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) - { - newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, true); - } - // If holding down control, round to the closest 50ms - else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) - { - newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0); - } - - if (_draggingStartSegment) - { - if (newTime < TimeSpan.FromMilliseconds(100)) - newTime = TimeSpan.FromMilliseconds(100); - SelectedProfileElement.StartSegmentLength = newTime; - } - else if (_draggingMainSegment) - { - if (newTime < SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100)) - newTime = SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100); - SelectedProfileElement.MainSegmentLength = newTime - SelectedProfileElement.StartSegmentLength; - } - else if (_draggingEndSegment) - { - if (newTime < SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100)) - newTime = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100); - SelectedProfileElement.EndSegmentLength = newTime - SelectedProfileElement.StartSegmentLength - SelectedProfileElement.MainSegmentLength; - } - } + EndTimelineSegmentViewModel.EnableSegment(); } #endregion diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml new file mode 100644 index 000000000..56d957874 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentView.xaml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs new file mode 100644 index 000000000..9175f2b64 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs @@ -0,0 +1,275 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using Artemis.Core.Models.Profile; +using Artemis.UI.Shared.Services.Interfaces; +using Artemis.UI.Shared.Utilities; +using Stylet; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline +{ + public class TimelineSegmentViewModel : PropertyChangedBase, IDisposable + { + private bool _draggingSegment; + private bool _showSegmentName; + private bool _showRepeatButton; + private bool _showDisableButton; + + public TimelineSegmentViewModel(IProfileEditorService profileEditorService, SegmentViewModelType segment) + { + ProfileEditorService = profileEditorService; + Segment = segment; + SelectedProfileElement = ProfileEditorService.SelectedProfileElement; + + switch (Segment) + { + case SegmentViewModelType.Start: + ToolTip = "This segment is played when a layer starts displaying because it's conditions are met"; + break; + case SegmentViewModelType.Main: + ToolTip = "This segment is played while a condition is met, either once or on a repeating loop"; + break; + case SegmentViewModelType.End: + ToolTip = "This segment is played once a condition is no longer met"; + break; + default: + throw new ArgumentOutOfRangeException(nameof(segment)); + } + + UpdateDisplay(); + ProfileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged; + SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged; + } + + public RenderProfileElement SelectedProfileElement { get; } + + public SegmentViewModelType Segment { get; } + public IProfileEditorService ProfileEditorService { get; } + public string ToolTip { get; } + + public TimeSpan SegmentLength + { + get + { + return Segment switch + { + SegmentViewModelType.Start => SelectedProfileElement?.StartSegmentLength ?? TimeSpan.Zero, + SegmentViewModelType.Main => SelectedProfileElement?.MainSegmentLength ?? TimeSpan.Zero, + SegmentViewModelType.End => SelectedProfileElement?.EndSegmentLength ?? TimeSpan.Zero, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + public double SegmentWidth => ProfileEditorService.PixelsPerSecond * SegmentLength.TotalSeconds; + + public bool SegmentEnabled => SegmentLength != TimeSpan.Zero; + public bool IsMainSegment => Segment == SegmentViewModelType.Main; + + // Only the main segment supports this, for any other segment the getter always returns false and the setter does nothing + public bool RepeatSegment + { + get + { + if (Segment != SegmentViewModelType.Main) + return false; + + return SelectedProfileElement?.RepeatMainSegment ?? false; + } + set + { + if (Segment != SegmentViewModelType.Main) + return; + + SelectedProfileElement.RepeatMainSegment = value; + ProfileEditorService.UpdateSelectedProfileElement(); + NotifyOfPropertyChange(nameof(RepeatSegment)); + } + } + + public double SegmentStartPosition + { + get + { + return Segment switch + { + SegmentViewModelType.Start => 0, + SegmentViewModelType.Main => ProfileEditorService.PixelsPerSecond * SelectedProfileElement.StartSegmentLength.TotalSeconds, + SegmentViewModelType.End => ProfileEditorService.PixelsPerSecond * (SelectedProfileElement.StartSegmentLength.TotalSeconds + SelectedProfileElement.MainSegmentLength.TotalSeconds), + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + public bool ShowSegmentName + { + get => _showSegmentName; + set => SetAndNotify(ref _showSegmentName, value); + } + + public bool ShowRepeatButton + { + get => _showRepeatButton; + set => SetAndNotify(ref _showRepeatButton, value); + } + + public bool ShowDisableButton + { + get => _showDisableButton; + set => SetAndNotify(ref _showDisableButton, value); + } + + public void Dispose() + { + ProfileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged; + SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged; + } + + public void DisableSegment() + { + switch (Segment) + { + case SegmentViewModelType.Start: + SelectedProfileElement.StartSegmentLength = TimeSpan.Zero; + break; + case SegmentViewModelType.Main: + SelectedProfileElement.MainSegmentLength = TimeSpan.Zero; + break; + case SegmentViewModelType.End: + SelectedProfileElement.EndSegmentLength = TimeSpan.Zero; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + NotifyOfPropertyChange(nameof(SegmentEnabled)); + ProfileEditorService.UpdateSelectedProfileElement(); + } + + public void EnableSegment() + { + switch (Segment) + { + case SegmentViewModelType.Start: + SelectedProfileElement.StartSegmentLength = TimeSpan.FromSeconds(1); + break; + case SegmentViewModelType.Main: + SelectedProfileElement.MainSegmentLength = TimeSpan.FromSeconds(1); + break; + case SegmentViewModelType.End: + SelectedProfileElement.EndSegmentLength = TimeSpan.FromSeconds(1); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + NotifyOfPropertyChange(nameof(SegmentEnabled)); + ProfileEditorService.UpdateSelectedProfileElement(); + } + + public void SegmentMouseDown(object sender, MouseButtonEventArgs e) + { + ((IInputElement) sender).CaptureMouse(); + _draggingSegment = true; + } + + public void SegmentMouseUp(object sender, MouseButtonEventArgs e) + { + ((IInputElement) sender).ReleaseMouseCapture(); + _draggingSegment = false; + } + + public void SegmentMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton != MouseButtonState.Pressed || !_draggingSegment) + return; + + // Get the parent scroll viewer, need that for our position + var parent = VisualTreeUtilities.FindParent((DependencyObject) sender, "TimelineHeaderScrollViewer"); + + var x = Math.Max(0, e.GetPosition(parent).X); + var newTime = TimeSpan.FromSeconds(x / ProfileEditorService.PixelsPerSecond); + + // Round the time to something that fits the current zoom level + if (ProfileEditorService.PixelsPerSecond < 200) + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0); + else if (ProfileEditorService.PixelsPerSecond < 500) + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0); + else + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds)); + + // If holding down shift, snap to the closest element on the timeline + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, true); + // If holding down control, round to the closest 50ms + else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0); + + + if (Segment == SegmentViewModelType.Start) + { + if (newTime < TimeSpan.FromMilliseconds(100)) + newTime = TimeSpan.FromMilliseconds(100); + SelectedProfileElement.StartSegmentLength = newTime; + } + else if (Segment == SegmentViewModelType.Main) + { + if (newTime < SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100)) + newTime = SelectedProfileElement.StartSegmentLength + TimeSpan.FromMilliseconds(100); + SelectedProfileElement.MainSegmentLength = newTime - SelectedProfileElement.StartSegmentLength; + } + else if (Segment == SegmentViewModelType.End) + { + if (newTime < SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100)) + newTime = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength + TimeSpan.FromMilliseconds(100); + SelectedProfileElement.EndSegmentLength = newTime - SelectedProfileElement.StartSegmentLength - SelectedProfileElement.MainSegmentLength; + } + + NotifyOfPropertyChange(nameof(SegmentLength)); + NotifyOfPropertyChange(nameof(SegmentWidth)); + + UpdateDisplay(); + } + + private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(RenderProfileElement.StartSegmentLength) || + e.PropertyName == nameof(RenderProfileElement.MainSegmentLength) || + e.PropertyName == nameof(RenderProfileElement.EndSegmentLength)) + { + NotifyOfPropertyChange(nameof(SegmentStartPosition)); + NotifyOfPropertyChange(nameof(SegmentWidth)); + } + } + + + private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e) + { + NotifyOfPropertyChange(nameof(SegmentWidth)); + NotifyOfPropertyChange(nameof(SegmentStartPosition)); + + UpdateDisplay(); + } + + private void UpdateDisplay() + { + if (!IsMainSegment) + ShowSegmentName = SegmentWidth > 60; + else + ShowSegmentName = SegmentWidth > 80; + + ShowRepeatButton = SegmentWidth > 45 && IsMainSegment; + ShowDisableButton = SegmentWidth > 25; + } + } + + public enum SegmentViewModelType + { + Start, + Main, + End + } +} \ No newline at end of file diff --git a/src/Plugins/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs b/src/Plugins/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs index f20b5635a..5aa063b2f 100644 --- a/src/Plugins/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs +++ b/src/Plugins/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs @@ -73,6 +73,19 @@ namespace Artemis.Plugins.LayerBrushes.Noise var height = (int) Math.Floor(path.Bounds.Height * _renderScale); CreateBitmap(width, height); + + var clipPath = new SKPath(Layer.Path); + Layer.ExcludePathFromTranslation(clipPath); + clipPath = new SKPath(clipPath); + 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++) @@ -84,6 +97,10 @@ namespace Artemis.Plugins.LayerBrushes.Noise 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) diff --git a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/GeneralDataModel.cs b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/GeneralDataModel.cs index 3d7453178..6828579e5 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/GeneralDataModel.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/GeneralDataModel.cs @@ -1,62 +1,9 @@ -using System.Collections.Generic; -using System.Diagnostics; -using Artemis.Core.Plugins.Abstract.DataModels.Attributes; -using Artemis.Plugins.Modules.General.DataModel.Windows; -using SkiaSharp; +using Artemis.Plugins.Modules.General.DataModel.Windows; namespace Artemis.Plugins.Modules.General.DataModel { public class GeneralDataModel : Core.Plugins.Abstract.DataModels.DataModel { - public TestDataModel TestDataModel { get; set; } - public WindowsDataModel Windows { get; set; } - - - public GeneralDataModel() - { - TestDataModel = new TestDataModel(); - Windows = new WindowsDataModel(); - } - } - - public class TestDataModel - { - public TestDataModel() - { - PlayerInfo = new PlayerInfo(); - IntsList = new List(); - PlayerInfosList = new List(); - } - - [DataModelProperty(Name = "A test string", Description = "This is a test string that's not of any use outside testing!")] - public string TestString { get; set; } - - [DataModelProperty(Name = "A test boolean", Description = "This is a test boolean that's not of any use outside testing!")] - public bool TestBoolean { get; set; } - - public SKColor TestColor { get; set; } = new SKColor(221, 21, 152); - - [DataModelProperty(Name = "Player info", Description = "[TEST] Contains information about the player")] - public PlayerInfo PlayerInfo { get; set; } - - public double UpdatesDividedByFour { get; set; } - public int Updates { get; set; } - - public List IntsList { get; set; } - public List PlayerInfosList { get; set; } - } - - public class PlayerInfo - { - [DataModelProperty(Name = "A test string", Description = "This is a test string that's not of any use outside testing!")] - public string TestString { get; set; } - - [DataModelProperty(Name = "A test boolean", Description = "This is a test boolean that's not of any use outside testing!")] - public bool TestBoolean { get; set; } - - [DataModelProperty(Affix = "%", MinValue = 0, MaxValue = 100)] - public int Health { get; set; } - - public SKPoint Position { get; set; } + public WindowDataModel ActiveWindow { get; set; } } } \ No newline at end of file diff --git a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs index 128295d79..17e07abd4 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowDataModel.cs @@ -16,7 +16,7 @@ namespace Artemis.Plugins.Modules.General.DataModel.Windows ProcessName = process.ProcessName; // Accessing MainModule requires admin privileges, this way does not - ProgramLocation = WindowMonitor.GetProcessFilename(process); + ProgramLocation = WindowUtilities.GetProcessFilename(process); } public string WindowTitle { get; set; } diff --git a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowsDataModel.cs b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowsDataModel.cs index e6b100d8b..b42815cb3 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowsDataModel.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/DataModel/Windows/WindowsDataModel.cs @@ -6,7 +6,6 @@ namespace Artemis.Plugins.Modules.General.DataModel.Windows { public class WindowsDataModel { - public WindowDataModel ActiveWindow { get; set; } - public List OpenWindows { get; set; } + } } diff --git a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs index 17062b515..a9e01ffed 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/GeneralModule.cs @@ -1,33 +1,16 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Interop; -using Artemis.Core; using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Abstract.ViewModels; -using Artemis.Core.Plugins.Models; using Artemis.Plugins.Modules.General.DataModel; using Artemis.Plugins.Modules.General.DataModel.Windows; using Artemis.Plugins.Modules.General.Utilities; using Artemis.Plugins.Modules.General.ViewModels; -using SkiaSharp; namespace Artemis.Plugins.Modules.General { public class GeneralModule : ProfileModule { - private readonly PluginSettings _settings; - private readonly Random _rand; - - - public GeneralModule(PluginSettings settings) - { - _settings = settings; - _rand = new Random(); - } - public override IEnumerable GetViewModels() { return new List {new GeneralViewModel(this)}; @@ -35,19 +18,7 @@ namespace Artemis.Plugins.Modules.General public override void Update(double deltaTime) { - DataModel.TestDataModel.UpdatesDividedByFour += 0.25; - DataModel.TestDataModel.Updates += 1; - DataModel.TestDataModel.PlayerInfo.Position = new SKPoint(_rand.Next(100), _rand.Next(100)); - DataModel.TestDataModel.PlayerInfo.Health++; - if (DataModel.TestDataModel.PlayerInfo.Health > 200) - DataModel.TestDataModel.PlayerInfo.Health = 0; - - DataModel.TestDataModel.IntsList[0] = _rand.Next(); - DataModel.TestDataModel.IntsList[2] = _rand.Next(); - UpdateCurrentWindow(); - UpdateBackgroundWindows(); - base.Update(deltaTime); } @@ -56,11 +27,6 @@ namespace Artemis.Plugins.Modules.General DisplayName = "General"; DisplayIcon = "AllInclusive"; ExpandsDataModel = true; - - DataModel.TestDataModel.IntsList = new List {_rand.Next(), _rand.Next(), _rand.Next()}; - DataModel.TestDataModel.PlayerInfosList = new List {new PlayerInfo()}; - - var testSetting = _settings.GetSetting("TestSetting", DateTime.Now); } public override void DisablePlugin() @@ -69,31 +35,11 @@ namespace Artemis.Plugins.Modules.General #region Open windows - private DateTime _lastBackgroundWindowsUpdate; - public void UpdateCurrentWindow() { - var processId = WindowMonitor.GetActiveProcessId(); - if (DataModel.Windows.ActiveWindow == null || DataModel.Windows.ActiveWindow.Process.Id != processId) - DataModel.Windows.ActiveWindow = new WindowDataModel(Process.GetProcessById(processId)); - } - - public void UpdateBackgroundWindows() - { - // This is kinda slow so lets not do it very often and lets do it in a task - if (DateTime.Now - _lastBackgroundWindowsUpdate < TimeSpan.FromSeconds(5)) - return; - - _lastBackgroundWindowsUpdate = DateTime.Now; - Task.Run(() => - { - // All processes with a main window handle are considered open windows - DataModel.Windows.OpenWindows = Process.GetProcesses() - .Where(p => p.MainWindowHandle != IntPtr.Zero) - .Select(p => new WindowDataModel(p)) - .Where(w => !string.IsNullOrEmpty(w.WindowTitle)) - .ToList(); - }); + var processId = WindowUtilities.GetActiveProcessId(); + if (DataModel.ActiveWindow == null || DataModel.ActiveWindow.Process.Id != processId) + DataModel.ActiveWindow = new WindowDataModel(Process.GetProcessById(processId)); } #endregion diff --git a/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowMonitor.cs b/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs similarity index 97% rename from src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowMonitor.cs rename to src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs index 02a4c5377..37267cc4f 100644 --- a/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowMonitor.cs +++ b/src/Plugins/Artemis.Plugins.Modules.General/Utilities/WindowUtilities.cs @@ -5,7 +5,7 @@ using System.Text; namespace Artemis.Plugins.Modules.General.Utilities { - public static class WindowMonitor + public static class WindowUtilities { public static int GetActiveProcessId() {