262 lines
8.4 KiB
C#
262 lines
8.4 KiB
C#
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
|
|
{
|
|
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;
|
|
}
|
|
} |