- add: sort visuals
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:InternalSortMethods"
|
||||
x:Class="InternalSortMethods.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
RequestedThemeVariant="Dark">
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
@@ -11,5 +10,34 @@
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
|
||||
<Style Selector="Window">
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
|
||||
<GradientStop Color="#0F0F1A" Offset="0"/>
|
||||
<GradientStop Color="#1A1A2E" Offset="0.5"/>
|
||||
<GradientStop Color="#16213E" Offset="1"/>
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ComboBox">
|
||||
<Setter Property="Background" Value="#20FFFFFF"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#40FFFFFF"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="MinHeight" Value="44"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="ComboBox:pointerover /template/ Border#NormalRectangle">
|
||||
<Setter Property="Background" Value="#30FFFFFF"/>
|
||||
<Setter Property="BorderBrush" Value="#60FFFFFF"/>
|
||||
</Style>
|
||||
|
||||
<Style Selector="Slider">
|
||||
<Setter Property="MinHeight" Value="44"/>
|
||||
</Style>
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
150
InternalSortMethods/Controls/ArrayVisualizer.cs
Normal file
150
InternalSortMethods/Controls/ArrayVisualizer.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace InternalSortMethods.Controls;
|
||||
|
||||
public class ArrayVisualizer : Control
|
||||
{
|
||||
public static readonly StyledProperty<ObservableCollection<Models.ArrayItemModel>?> ItemsProperty =
|
||||
AvaloniaProperty.Register<ArrayVisualizer, ObservableCollection<Models.ArrayItemModel>?>(nameof(Items));
|
||||
|
||||
public ObservableCollection<Models.ArrayItemModel>? Items
|
||||
{
|
||||
get => GetValue(ItemsProperty);
|
||||
set => SetValue(ItemsProperty, value);
|
||||
}
|
||||
|
||||
static ArrayVisualizer()
|
||||
{
|
||||
ItemsProperty.Changed.AddClassHandler<ArrayVisualizer>((x, e) => x.OnItemsChanged(e));
|
||||
}
|
||||
|
||||
public ArrayVisualizer()
|
||||
{
|
||||
ClipToBounds = true;
|
||||
}
|
||||
|
||||
private void OnItemsChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.OldValue is ObservableCollection<Models.ArrayItemModel> oldCollection)
|
||||
{
|
||||
oldCollection.CollectionChanged -= OnCollectionChanged;
|
||||
foreach (var item in oldCollection)
|
||||
item.PropertyChanged -= OnItemPropertyChanged;
|
||||
}
|
||||
|
||||
if (e.NewValue is ObservableCollection<Models.ArrayItemModel> newCollection)
|
||||
{
|
||||
newCollection.CollectionChanged += OnCollectionChanged;
|
||||
foreach (var item in newCollection)
|
||||
item.PropertyChanged += OnItemPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (Models.ArrayItemModel item in e.NewItems)
|
||||
item.PropertyChanged += OnItemPropertyChanged;
|
||||
}
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (Models.ArrayItemModel item in e.OldItems)
|
||||
item.PropertyChanged -= OnItemPropertyChanged;
|
||||
}
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private void OnItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs? e)
|
||||
{
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
if (Items == null || Items.Count == 0 || Bounds.Width <= 0 || Bounds.Height <= 0)
|
||||
return;
|
||||
|
||||
double width = Bounds.Width;
|
||||
double height = Bounds.Height;
|
||||
int count = Items.Count;
|
||||
double barWidth = (width - (count - 1) * 2) / count;
|
||||
double maxValue = 100.0;
|
||||
|
||||
var backgroundBrush = new SolidColorBrush(Color.Parse("#1E1E2E"));
|
||||
context.DrawRectangle(backgroundBrush, null, new Rect(0, 0, width, height));
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var item = Items[i];
|
||||
double barHeight = (item.Value / maxValue) * height * 0.9;
|
||||
double x = i * (barWidth + 2);
|
||||
double y = height - barHeight;
|
||||
|
||||
var brush = GetBrushForState(item.State);
|
||||
context.DrawRectangle(brush, null, new Rect(x, y, barWidth, barHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private IBrush GetBrushForState(Models.ArrayItemState state)
|
||||
{
|
||||
return state switch
|
||||
{
|
||||
Models.ArrayItemState.Comparing => new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new RelativePoint(0, 1, RelativeUnit.Absolute),
|
||||
EndPoint = new RelativePoint(0, 0, RelativeUnit.Absolute),
|
||||
GradientStops =
|
||||
[
|
||||
new GradientStop(Color.Parse("#FF6B6B"), 0),
|
||||
new GradientStop(Color.Parse("#FF8E8E"), 1)
|
||||
]
|
||||
},
|
||||
Models.ArrayItemState.Swapping => new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new RelativePoint(0, 1, RelativeUnit.Absolute),
|
||||
EndPoint = new RelativePoint(0, 0, RelativeUnit.Absolute),
|
||||
GradientStops =
|
||||
[
|
||||
new GradientStop(Color.Parse("#FFE66D"), 0),
|
||||
new GradientStop(Color.Parse("#FFB347"), 1)
|
||||
]
|
||||
},
|
||||
Models.ArrayItemState.Sorted => new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new RelativePoint(0, 1, RelativeUnit.Absolute),
|
||||
EndPoint = new RelativePoint(0, 0, RelativeUnit.Absolute),
|
||||
GradientStops =
|
||||
[
|
||||
new GradientStop(Color.Parse("#4ADE80"), 0),
|
||||
new GradientStop(Color.Parse("#22C55E"), 1)
|
||||
]
|
||||
},
|
||||
Models.ArrayItemState.Pivot => new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new RelativePoint(0, 1, RelativeUnit.Absolute),
|
||||
EndPoint = new RelativePoint(0, 0, RelativeUnit.Absolute),
|
||||
GradientStops =
|
||||
[
|
||||
new GradientStop(Color.Parse("#C084FC"), 0),
|
||||
new GradientStop(Color.Parse("#A855F7"), 1)
|
||||
]
|
||||
},
|
||||
_ => new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new RelativePoint(0, 1, RelativeUnit.Absolute),
|
||||
EndPoint = new RelativePoint(0, 0, RelativeUnit.Absolute),
|
||||
GradientStops =
|
||||
[
|
||||
new GradientStop(Color.Parse("#6366F1"), 0),
|
||||
new GradientStop(Color.Parse("#4F46E5"), 1)
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
38
InternalSortMethods/Models/SortAlgorithmsProvider.cs
Normal file
38
InternalSortMethods/Models/SortAlgorithmsProvider.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
public static class SortAlgorithmsProvider
|
||||
{
|
||||
public static IReadOnlyList<SortingAlgorithm> Algorithms { get; } =
|
||||
[
|
||||
new BubbleSort(),
|
||||
new SelectionSort(),
|
||||
new InsertionSort(),
|
||||
new QuickSort(),
|
||||
new MergeSort(),
|
||||
new HeapSort()
|
||||
];
|
||||
}
|
||||
|
||||
public partial class ArrayItemModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
private int _value;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _index;
|
||||
|
||||
[ObservableProperty]
|
||||
private ArrayItemState _state = ArrayItemState.Normal;
|
||||
}
|
||||
|
||||
public enum ArrayItemState
|
||||
{
|
||||
Normal,
|
||||
Comparing,
|
||||
Swapping,
|
||||
Sorted,
|
||||
Pivot
|
||||
}
|
||||
22
InternalSortMethods/Models/SortStep.cs
Normal file
22
InternalSortMethods/Models/SortStep.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
public enum SortActionType
|
||||
{
|
||||
Compare,
|
||||
Swap,
|
||||
Set,
|
||||
MarkSorted,
|
||||
MarkPivot
|
||||
}
|
||||
|
||||
public record SortStep(int Index1, int Index2, SortActionType Action, int[]? ArraySnapshot = null);
|
||||
|
||||
public record SortState(int[] Array, List<SortStep> Steps, int CurrentStep, bool IsComplete);
|
||||
|
||||
public class SortingResult
|
||||
{
|
||||
public required int[] SortedArray { get; init; }
|
||||
public required List<SortStep> Steps { get; init; } = [];
|
||||
}
|
||||
277
InternalSortMethods/Models/SortingAlgorithms.cs
Normal file
277
InternalSortMethods/Models/SortingAlgorithms.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace InternalSortMethods.Models;
|
||||
|
||||
public abstract class SortingAlgorithm
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract string NameRu { get; }
|
||||
|
||||
protected List<SortStep> Steps { get; } = [];
|
||||
protected int[] _array = [];
|
||||
|
||||
public SortingResult Sort(int[] array)
|
||||
{
|
||||
_array = (int[])array.Clone();
|
||||
Steps.Clear();
|
||||
OnSort();
|
||||
return new SortingResult
|
||||
{
|
||||
SortedArray = _array,
|
||||
Steps = [.. Steps]
|
||||
};
|
||||
}
|
||||
|
||||
protected void Compare(int i, int j)
|
||||
{
|
||||
Steps.Add(new SortStep(i, j, SortActionType.Compare));
|
||||
}
|
||||
|
||||
protected void Swap(int i, int j)
|
||||
{
|
||||
(_array[i], _array[j]) = (_array[j], _array[i]);
|
||||
Steps.Add(new SortStep(i, j, SortActionType.Swap, (int[])_array.Clone()));
|
||||
}
|
||||
|
||||
protected void SetValue(int index, int value)
|
||||
{
|
||||
_array[index] = value;
|
||||
Steps.Add(new SortStep(index, -1, SortActionType.Set, (int[])_array.Clone()));
|
||||
}
|
||||
|
||||
protected void MarkSorted(int index)
|
||||
{
|
||||
Steps.Add(new SortStep(index, -1, SortActionType.MarkSorted));
|
||||
}
|
||||
|
||||
protected void MarkPivot(int index)
|
||||
{
|
||||
Steps.Add(new SortStep(index, -1, SortActionType.MarkPivot));
|
||||
}
|
||||
|
||||
protected abstract void OnSort();
|
||||
}
|
||||
|
||||
public class BubbleSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Bubble Sort";
|
||||
public override string NameRu => "Пузырьковая";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
int n = _array.Length;
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
bool swapped = false;
|
||||
for (int j = 0; j < n - i - 1; j++)
|
||||
{
|
||||
Compare(j, j + 1);
|
||||
if (_array[j] > _array[j + 1])
|
||||
{
|
||||
Swap(j, j + 1);
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
MarkSorted(n - i - 1);
|
||||
if (!swapped) break;
|
||||
}
|
||||
MarkSorted(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectionSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Selection Sort";
|
||||
public override string NameRu => "Выбором";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
int n = _array.Length;
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
int minIdx = i;
|
||||
for (int j = i + 1; j < n; j++)
|
||||
{
|
||||
Compare(j, minIdx);
|
||||
if (_array[j] < _array[minIdx])
|
||||
minIdx = j;
|
||||
}
|
||||
if (minIdx != i)
|
||||
Swap(i, minIdx);
|
||||
MarkSorted(i);
|
||||
}
|
||||
MarkSorted(n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public class InsertionSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Insertion Sort";
|
||||
public override string NameRu => "Вставками";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
int n = _array.Length;
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
int key = _array[i];
|
||||
int j = i - 1;
|
||||
Compare(j, i);
|
||||
while (j >= 0 && _array[j] > key)
|
||||
{
|
||||
SetValue(j + 1, _array[j]);
|
||||
j--;
|
||||
if (j >= 0) Compare(j, i);
|
||||
}
|
||||
SetValue(j + 1, key);
|
||||
}
|
||||
MarkSorted(0);
|
||||
}
|
||||
}
|
||||
|
||||
public class QuickSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Quick Sort";
|
||||
public override string NameRu => "Быстрая";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
QuickSortRecursive(0, _array.Length - 1);
|
||||
MarkSorted(0);
|
||||
}
|
||||
|
||||
private void QuickSortRecursive(int low, int high)
|
||||
{
|
||||
if (low < high)
|
||||
{
|
||||
int pi = Partition(low, high);
|
||||
QuickSortRecursive(low, pi - 1);
|
||||
QuickSortRecursive(pi + 1, high);
|
||||
}
|
||||
}
|
||||
|
||||
private int Partition(int low, int high)
|
||||
{
|
||||
int pivot = _array[high];
|
||||
MarkPivot(high);
|
||||
int i = low - 1;
|
||||
for (int j = low; j < high; j++)
|
||||
{
|
||||
Compare(j, high);
|
||||
if (_array[j] < pivot)
|
||||
{
|
||||
i++;
|
||||
if (i != j) Swap(i, j);
|
||||
}
|
||||
}
|
||||
if (i + 1 != high) Swap(i + 1, high);
|
||||
MarkSorted(i + 1);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public class MergeSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Merge Sort";
|
||||
public override string NameRu => "Слиянием";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
MergeSortRecursive(0, _array.Length - 1);
|
||||
MarkSorted(0);
|
||||
}
|
||||
|
||||
private void MergeSortRecursive(int left, int right)
|
||||
{
|
||||
if (left < right)
|
||||
{
|
||||
int mid = left + (right - left) / 2;
|
||||
MergeSortRecursive(left, mid);
|
||||
MergeSortRecursive(mid + 1, right);
|
||||
Merge(left, mid, right);
|
||||
}
|
||||
}
|
||||
|
||||
private void Merge(int left, int mid, int right)
|
||||
{
|
||||
int n1 = mid - left + 1;
|
||||
int n2 = right - mid;
|
||||
int[] leftArr = new int[n1];
|
||||
int[] rightArr = new int[n2];
|
||||
|
||||
for (int x = 0; x < n1; x++) leftArr[x] = _array[left + x];
|
||||
for (int x = 0; x < n2; x++) rightArr[x] = _array[mid + 1 + x];
|
||||
|
||||
int i = 0, j = 0, k = left;
|
||||
while (i < n1 && j < n2)
|
||||
{
|
||||
Compare(left + i, mid + 1 + j);
|
||||
if (leftArr[i] <= rightArr[j])
|
||||
{
|
||||
SetValue(k, leftArr[i]);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValue(k, rightArr[j]);
|
||||
j++;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
while (i < n1)
|
||||
{
|
||||
SetValue(k, leftArr[i]);
|
||||
i++; k++;
|
||||
}
|
||||
while (j < n2)
|
||||
{
|
||||
SetValue(k, rightArr[j]);
|
||||
j++; k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HeapSort : SortingAlgorithm
|
||||
{
|
||||
public override string Name => "Heap Sort";
|
||||
public override string NameRu => "Кучей";
|
||||
|
||||
protected override void OnSort()
|
||||
{
|
||||
int n = _array.Length;
|
||||
for (int i = n / 2 - 1; i >= 0; i--)
|
||||
Heapify(n, i);
|
||||
for (int i = n - 1; i > 0; i--)
|
||||
{
|
||||
Swap(0, i);
|
||||
MarkSorted(i);
|
||||
Heapify(i, 0);
|
||||
}
|
||||
MarkSorted(0);
|
||||
}
|
||||
|
||||
private void Heapify(int heapSize, int root)
|
||||
{
|
||||
int largest = root;
|
||||
int left = 2 * root + 1;
|
||||
int right = 2 * root + 2;
|
||||
|
||||
if (left < heapSize)
|
||||
{
|
||||
Compare(left, largest);
|
||||
if (_array[left] > _array[largest])
|
||||
largest = left;
|
||||
}
|
||||
if (right < heapSize)
|
||||
{
|
||||
Compare(right, largest);
|
||||
if (_array[right] > _array[largest])
|
||||
largest = right;
|
||||
}
|
||||
if (largest != root)
|
||||
{
|
||||
Swap(root, largest);
|
||||
Heapify(heapSize, largest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,262 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using InternalSortMethods.Models;
|
||||
|
||||
namespace InternalSortMethods.ViewModels;
|
||||
|
||||
public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
[ObservableProperty] private string _greeting = "Welcome to Avalonia!";
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private SortingResult? _currentResult;
|
||||
private int _currentStepIndex;
|
||||
private readonly ManualResetEventSlim _pauseEvent = new(true);
|
||||
|
||||
[ObservableProperty] private ObservableCollection<ArrayItemModel> _arrayItems = [];
|
||||
[ObservableProperty] private ObservableCollection<SortingAlgorithm> _algorithms = [];
|
||||
[ObservableProperty] private SortingAlgorithm? _selectedAlgorithm;
|
||||
[ObservableProperty] private int _arraySize = 20;
|
||||
[ObservableProperty] private double _animationSpeed = 500;
|
||||
[ObservableProperty] private bool _isRunning;
|
||||
[ObservableProperty] private bool _isPaused;
|
||||
[ObservableProperty] private bool _isComplete;
|
||||
[ObservableProperty] private string _statusText = "Готов";
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
foreach (var algo in SortAlgorithmsProvider.Algorithms)
|
||||
Algorithms.Add(algo);
|
||||
SelectedAlgorithm = Algorithms[0];
|
||||
GenerateArray();
|
||||
}
|
||||
|
||||
partial void OnSelectedAlgorithmChanged(SortingAlgorithm? value)
|
||||
{
|
||||
if (value != null && !IsRunning)
|
||||
ResetCommand.Execute(null);
|
||||
}
|
||||
|
||||
partial void OnArraySizeChanged(int value)
|
||||
{
|
||||
if (!IsRunning)
|
||||
GenerateArray();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void GenerateArray()
|
||||
{
|
||||
ArrayItems.Clear();
|
||||
var random = new Random();
|
||||
int maxVal = 100;
|
||||
for (int i = 0; i < ArraySize; i++)
|
||||
{
|
||||
ArrayItems.Add(new ArrayItemModel
|
||||
{
|
||||
Value = random.Next(10, maxVal),
|
||||
Index = i,
|
||||
State = ArrayItemState.Normal
|
||||
});
|
||||
}
|
||||
ResetStates();
|
||||
StatusText = "Массив сгенерирован";
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanStart))]
|
||||
private async Task StartAsync()
|
||||
{
|
||||
if (IsComplete || _currentResult == null)
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _cancellationTokenSource.Token;
|
||||
|
||||
IsRunning = true;
|
||||
IsComplete = false;
|
||||
StatusText = $"Сортировка: {SelectedAlgorithm?.NameRu}";
|
||||
|
||||
var arr = GetCurrentArray();
|
||||
_currentResult = SelectedAlgorithm!.Sort(arr);
|
||||
_currentStepIndex = 0;
|
||||
|
||||
_ = Task.Run(() => ProcessStepsAsync(token), token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pauseEvent.Set();
|
||||
IsPaused = false;
|
||||
StatusText = $"Продолжено";
|
||||
}
|
||||
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
PauseCommand.NotifyCanExecuteChanged();
|
||||
StepForwardCommand.NotifyCanExecuteChanged();
|
||||
ResetCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private bool CanStart() => !IsRunning || IsPaused || IsComplete;
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanPause))]
|
||||
private void Pause()
|
||||
{
|
||||
_pauseEvent.Reset();
|
||||
IsPaused = true;
|
||||
StatusText = "Пауза";
|
||||
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
PauseCommand.NotifyCanExecuteChanged();
|
||||
StepForwardCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private bool CanPause() => IsRunning && !IsPaused;
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanStep))]
|
||||
private async Task StepForward()
|
||||
{
|
||||
if (_currentResult == null || _currentStepIndex >= _currentResult.Steps.Count)
|
||||
return;
|
||||
|
||||
_pauseEvent.Reset();
|
||||
IsPaused = true;
|
||||
|
||||
await ApplyStepAsync(_currentResult.Steps[_currentStepIndex]);
|
||||
_currentStepIndex++;
|
||||
|
||||
if (_currentStepIndex >= _currentResult.Steps.Count)
|
||||
Complete();
|
||||
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
PauseCommand.NotifyCanExecuteChanged();
|
||||
StepForwardCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private bool CanStep() => IsRunning && _currentResult != null && _currentStepIndex < _currentResult.Steps.Count;
|
||||
|
||||
[RelayCommand]
|
||||
private void Reset()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = null;
|
||||
_currentResult = null;
|
||||
_currentStepIndex = 0;
|
||||
IsRunning = false;
|
||||
IsPaused = false;
|
||||
IsComplete = false;
|
||||
_pauseEvent.Set();
|
||||
|
||||
ResetStates();
|
||||
StatusText = "Сброшено";
|
||||
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
PauseCommand.NotifyCanExecuteChanged();
|
||||
StepForwardCommand.NotifyCanExecuteChanged();
|
||||
ResetCommand.NotifyCanExecuteChanged();
|
||||
}
|
||||
|
||||
private async Task ProcessStepsAsync(CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_currentStepIndex < _currentResult!.Steps.Count && !token.IsCancellationRequested)
|
||||
{
|
||||
_pauseEvent.Wait(token);
|
||||
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
await ApplyStepAsync(_currentResult.Steps[_currentStepIndex]);
|
||||
_currentStepIndex++;
|
||||
|
||||
await Task.Delay((int)AnimationSpeed, token);
|
||||
}
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
Complete();
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
private async Task ApplyStepAsync(SortStep step)
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
for (int i = 0; i < ArrayItems.Count; i++)
|
||||
{
|
||||
if (ArrayItems[i].State != ArrayItemState.Sorted)
|
||||
ArrayItems[i].State = ArrayItemState.Normal;
|
||||
}
|
||||
|
||||
int i1 = step.Index1;
|
||||
int i2 = step.Index2;
|
||||
|
||||
switch (step.Action)
|
||||
{
|
||||
case SortActionType.Compare:
|
||||
if (i1 >= 0 && i1 < ArrayItems.Count)
|
||||
ArrayItems[i1].State = ArrayItemState.Comparing;
|
||||
if (i2 >= 0 && i2 < ArrayItems.Count)
|
||||
ArrayItems[i2].State = ArrayItemState.Comparing;
|
||||
break;
|
||||
|
||||
case SortActionType.Swap:
|
||||
if (i1 >= 0 && i1 < ArrayItems.Count && i2 >= 0 && i2 < ArrayItems.Count)
|
||||
{
|
||||
(ArrayItems[i1].Value, ArrayItems[i2].Value) = (ArrayItems[i2].Value, ArrayItems[i1].Value);
|
||||
ArrayItems[i1].State = ArrayItemState.Swapping;
|
||||
ArrayItems[i2].State = ArrayItemState.Swapping;
|
||||
}
|
||||
break;
|
||||
|
||||
case SortActionType.Set:
|
||||
if (i1 >= 0 && i1 < ArrayItems.Count && step.ArraySnapshot != null && i1 < step.ArraySnapshot.Length)
|
||||
ArrayItems[i1].Value = step.ArraySnapshot[i1];
|
||||
break;
|
||||
|
||||
case SortActionType.MarkSorted:
|
||||
if (i1 >= 0 && i1 < ArrayItems.Count)
|
||||
ArrayItems[i1].State = ArrayItemState.Sorted;
|
||||
break;
|
||||
|
||||
case SortActionType.MarkPivot:
|
||||
if (i1 >= 0 && i1 < ArrayItems.Count)
|
||||
ArrayItems[i1].State = ArrayItemState.Pivot;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void Complete()
|
||||
{
|
||||
IsRunning = false;
|
||||
IsComplete = true;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
foreach (var item in ArrayItems)
|
||||
item.State = ArrayItemState.Sorted;
|
||||
|
||||
StatusText = "Сортировка завершена!";
|
||||
|
||||
StartCommand.NotifyCanExecuteChanged();
|
||||
PauseCommand.NotifyCanExecuteChanged();
|
||||
StepForwardCommand.NotifyCanExecuteChanged();
|
||||
ResetCommand.NotifyCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private int[] GetCurrentArray()
|
||||
{
|
||||
int[] arr = new int[ArrayItems.Count];
|
||||
for (int i = 0; i < ArrayItems.Count; i++)
|
||||
arr[i] = ArrayItems[i].Value;
|
||||
return arr;
|
||||
}
|
||||
|
||||
private void ResetStates()
|
||||
{
|
||||
foreach (var item in ArrayItems)
|
||||
item.State = ArrayItemState.Normal;
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,110 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:InternalSortMethods.ViewModels"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
xmlns:controls="using:InternalSortMethods.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="InternalSortMethods.Views.MainView"
|
||||
x:DataType="vm:MainViewModel">
|
||||
<Design.DataContext>
|
||||
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</UserControl>
|
||||
<UserControl.Resources>
|
||||
<ControlTheme x:Key="GlassButton" TargetType="Button">
|
||||
<Setter Property="Background" Value="#40666EF1"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="CornerRadius" Value="12"/>
|
||||
<Setter Property="Padding" Value="16,12"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="MinHeight" Value="48"/>
|
||||
<Setter Property="MinWidth" Value="80"/>
|
||||
</ControlTheme>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<Border Grid.Row="0" Background="#20000000" CornerRadius="16" Margin="16,16,16,8" Padding="16">
|
||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,*,Auto">
|
||||
<StackPanel Grid.Row="0" Grid.Column="0" Spacing="12">
|
||||
<TextBlock Text="Алгоритм сортировки" FontSize="12" Foreground="#AAFFFFFF"/>
|
||||
<ComboBox ItemsSource="{Binding Algorithms}"
|
||||
SelectedItem="{Binding SelectedAlgorithm}"
|
||||
MinWidth="180" MinHeight="44"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding NameRu}"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Spacing="12" Margin="16,0">
|
||||
<TextBlock Text="Размер массива" FontSize="12" Foreground="#AAFFFFFF"/>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Slider Grid.Column="0" Minimum="5" Maximum="50" Value="{Binding ArraySize}"
|
||||
TickFrequency="5" IsSnapToTickEnabled="True" MinHeight="44"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding ArraySize}" Width="30" Margin="8,0"
|
||||
VerticalAlignment="Center" Foreground="White"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Row="0" Grid.Column="2" Content="Новый массив"
|
||||
Command="{Binding GenerateArrayCommand}"
|
||||
Theme="{StaticResource GlassButton}"
|
||||
VerticalAlignment="Bottom" MinWidth="120"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.ColumnSpan="3" Spacing="12" Margin="0,16,0,0">
|
||||
<TextBlock Text="Скорость анимации" FontSize="12" Foreground="#AAFFFFFF"/>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<Slider Grid.Column="0" Minimum="50" Maximum="1000" Value="{Binding AnimationSpeed}"
|
||||
TickFrequency="50" IsSnapToTickEnabled="True" MinHeight="44"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding AnimationSpeed, StringFormat={}{0:0}} мс" Width="50" Margin="8,0"
|
||||
VerticalAlignment="Center" Foreground="White"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1" Background="#15FFFFFF" CornerRadius="16" Margin="16,8" Padding="16">
|
||||
<controls:ArrayVisualizer Items="{Binding ArrayItems}"/>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="2" Background="#20000000" CornerRadius="16" Margin="16,8,16,16" Padding="16">
|
||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,*,*,*,*">
|
||||
<TextBlock Grid.Row="0" Grid.ColumnSpan="5" Text="{Binding StatusText}"
|
||||
FontSize="16" FontWeight="SemiBold" Foreground="White"
|
||||
HorizontalAlignment="Center" Margin="0,0,0,16"/>
|
||||
|
||||
<Button Grid.Row="1" Grid.Column="0" Content="Старт"
|
||||
Command="{Binding StartCommand}"
|
||||
Theme="{StaticResource GlassButton}"
|
||||
HorizontalAlignment="Stretch" Margin="0,0,8,0"/>
|
||||
|
||||
<Button Grid.Row="1" Grid.Column="1" Content="Пауза"
|
||||
Command="{Binding PauseCommand}"
|
||||
Theme="{StaticResource GlassButton}"
|
||||
HorizontalAlignment="Stretch" Margin="0,0,8,0"/>
|
||||
|
||||
<Button Grid.Row="1" Grid.Column="2" Content="Шаг"
|
||||
Command="{Binding StepForwardCommand}"
|
||||
Theme="{StaticResource GlassButton}"
|
||||
HorizontalAlignment="Stretch" Margin="0,0,8,0"/>
|
||||
|
||||
<Button Grid.Row="1" Grid.Column="3" Content="Сброс"
|
||||
Command="{Binding ResetCommand}"
|
||||
Theme="{StaticResource GlassButton}"
|
||||
HorizontalAlignment="Stretch" Margin="0,0,8,0"/>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="4" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Ellipse Width="12" Height="12" Fill="#4ADE80" Margin="0,0,8,0"
|
||||
IsVisible="{Binding IsComplete}"/>
|
||||
<Ellipse Width="12" Height="12" Fill="#FFE66D" Margin="0,0,8,0"
|
||||
IsVisible="{Binding IsPaused}"/>
|
||||
<Ellipse Width="12" Height="12" Fill="#6366F1"
|
||||
IsVisible="{Binding IsRunning}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
Reference in New Issue
Block a user