- add: sort visuals
This commit is contained in:
@@ -2,8 +2,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:InternalSortMethods"
|
xmlns:local="using:InternalSortMethods"
|
||||||
x:Class="InternalSortMethods.App"
|
x:Class="InternalSortMethods.App"
|
||||||
RequestedThemeVariant="Default">
|
RequestedThemeVariant="Dark">
|
||||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
|
||||||
|
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
<local:ViewLocator/>
|
<local:ViewLocator/>
|
||||||
@@ -11,5 +10,34 @@
|
|||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<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.Styles>
|
||||||
</Application>
|
</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;
|
namespace InternalSortMethods.ViewModels;
|
||||||
|
|
||||||
public partial class MainViewModel : ViewModelBase
|
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:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:InternalSortMethods.ViewModels"
|
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:Class="InternalSortMethods.Views.MainView"
|
||||||
x:DataType="vm:MainViewModel">
|
x:DataType="vm:MainViewModel">
|
||||||
<Design.DataContext>
|
<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 />
|
<vm:MainViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
|
||||||
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<UserControl.Resources>
|
||||||
</UserControl>
|
<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