Skip to content

Commit 1c6eec6

Browse files
committed
Add basic, experimental entity sorting
1 parent dda7864 commit 1c6eec6

9 files changed

Lines changed: 333 additions & 34 deletions

File tree

‎Torch.Server/ViewModels/Entities/EntityViewModel.cs‎

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
using System.Windows.Controls;
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Windows.Controls;
5+
using NLog;
6+
using Sandbox.Game.Entities;
27
using Sandbox.Game.World;
38
using Torch.API.Managers;
49
using Torch.Collections;
510
using Torch.Server.Managers;
11+
using Torch.Utils;
612
using VRage.Game.Entity;
713
using VRage.Game.ModAPI;
814
using VRage.ModAPI;
@@ -14,6 +20,8 @@ public class EntityViewModel : ViewModel
1420
{
1521
protected EntityTreeViewModel Tree { get; }
1622

23+
private static Logger Log = LogManager.GetCurrentClassLogger();
24+
1725
private IMyEntity _backing;
1826
public IMyEntity Entity
1927
{
@@ -43,6 +51,75 @@ public virtual string Name
4351
}
4452
}
4553

54+
private string _descriptiveName;
55+
public string DescriptiveName
56+
{
57+
get => _descriptiveName ?? (_descriptiveName = GetSortedName(EntityTreeViewModel.SortEnum.Name));
58+
set => _descriptiveName = value;
59+
}
60+
61+
public virtual string GetSortedName(EntityTreeViewModel.SortEnum sort)
62+
{
63+
switch (sort)
64+
{
65+
case EntityTreeViewModel.SortEnum.Name:
66+
return Name;
67+
case EntityTreeViewModel.SortEnum.Size:
68+
return $"{Name} ({Entity.WorldVolume.Radius * 2:N}m)";
69+
case EntityTreeViewModel.SortEnum.Speed:
70+
return $"{Name} ({Entity.Physics?.LinearVelocity.Length() ?? 0:N}m/s)";
71+
case EntityTreeViewModel.SortEnum.BlockCount:
72+
if (Entity is MyCubeGrid grid)
73+
return $"{Name} ({grid.BlocksCount} blocks)";
74+
return Name;
75+
case EntityTreeViewModel.SortEnum.DistFromCenter:
76+
return $"{Name} ({Entity.GetPosition().Length():N}m)";
77+
case EntityTreeViewModel.SortEnum.Owner:
78+
if (Entity is MyCubeGrid g)
79+
return $"{Name} ({g.GetGridOwnerName()})";
80+
return Name;
81+
default:
82+
throw new ArgumentOutOfRangeException(nameof(sort), sort, null);
83+
}
84+
}
85+
86+
public virtual int CompareToSort(EntityViewModel other, EntityTreeViewModel.SortEnum sort)
87+
{
88+
switch (sort)
89+
{
90+
case EntityTreeViewModel.SortEnum.Name:
91+
return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase);
92+
case EntityTreeViewModel.SortEnum.Size:
93+
return Entity.WorldVolume.Radius.CompareTo(other.Entity.WorldVolume.Radius);
94+
case EntityTreeViewModel.SortEnum.Speed:
95+
if (Entity.Physics == null)
96+
{
97+
if (other.Entity.Physics == null)
98+
return 0;
99+
return -1;
100+
}
101+
if (other.Entity.Physics == null)
102+
return 1;
103+
return Entity.Physics.LinearVelocity.LengthSquared().CompareTo(other.Entity.Physics.LinearVelocity.LengthSquared());
104+
case EntityTreeViewModel.SortEnum.BlockCount:
105+
{
106+
if (Entity is MyCubeGrid ga && other.Entity is MyCubeGrid gb)
107+
return ga.BlocksCount.CompareTo(gb.BlocksCount);
108+
goto case EntityTreeViewModel.SortEnum.Name;
109+
}
110+
case EntityTreeViewModel.SortEnum.DistFromCenter:
111+
return Entity.GetPosition().LengthSquared().CompareTo(other.Entity.GetPosition().LengthSquared());
112+
case EntityTreeViewModel.SortEnum.Owner:
113+
{
114+
if (Entity is MyCubeGrid ga && other.Entity is MyCubeGrid gb)
115+
return string.Compare(ga.GetGridOwnerName(), gb.GetGridOwnerName(), StringComparison.InvariantCultureIgnoreCase);
116+
goto case EntityTreeViewModel.SortEnum.Name;
117+
}
118+
default:
119+
throw new ArgumentOutOfRangeException(nameof(sort), sort, null);
120+
}
121+
}
122+
46123
public virtual string Position
47124
{
48125
get => Entity?.GetPosition().ToString();
@@ -76,5 +153,20 @@ public EntityViewModel()
76153
{
77154

78155
}
156+
157+
public class Comparer : IComparer<EntityViewModel>
158+
{
159+
private EntityTreeViewModel.SortEnum _sort;
160+
161+
public Comparer(EntityTreeViewModel.SortEnum sort)
162+
{
163+
_sort = sort;
164+
}
165+
166+
public int Compare(EntityViewModel x, EntityViewModel y)
167+
{
168+
return x.CompareToSort(y, _sort);
169+
}
170+
}
79171
}
80172
}

‎Torch.Server/ViewModels/Entities/GridViewModel.cs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,14 @@ public MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDic
5050
Blocks { get; } =
5151
new MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>(
5252
CubeBlockDefinitionComparer.Default);
53-
54-
/// <inheritdoc />
55-
public string DescriptiveName { get; }
56-
53+
5754
public GridViewModel()
5855
{
5956
}
6057

6158
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
6259
{
63-
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
60+
//DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
6461
Blocks.Add(_fillerDefinition, new MtObservableSortedDictionary<long, BlockViewModel>());
6562
}
6663

‎Torch.Server/ViewModels/EntityTreeViewModel.cs‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,21 @@
1212
using System.Windows.Threading;
1313
using NLog;
1414
using Torch.Collections;
15+
using Torch.Server.Views.Entities;
1516

1617
namespace Torch.Server.ViewModels
1718
{
1819
public class EntityTreeViewModel : ViewModel
1920
{
21+
public enum SortEnum
22+
{
23+
Name,
24+
Size,
25+
Speed,
26+
Owner,
27+
BlockCount,
28+
DistFromCenter,
29+
}
2030
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
2131

2232
//TODO: these should be sorted sets for speed
@@ -26,7 +36,13 @@ public class EntityTreeViewModel : ViewModel
2636
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
2737
public Dispatcher ControlDispatcher => _control.Dispatcher;
2838

39+
public SortedView<GridViewModel> SortedGrids { get; }
40+
public SortedView<CharacterViewModel> SortedCharacters { get; }
41+
public SortedView<EntityViewModel> SortedFloatingObjects { get; }
42+
public SortedView<VoxelMapViewModel> SortedVoxelMaps { get; }
43+
2944
private EntityViewModel _currentEntity;
45+
private SortEnum _currentSort;
3046
private UserControl _control;
3147

3248
public EntityViewModel CurrentEntity
@@ -35,6 +51,12 @@ public EntityViewModel CurrentEntity
3551
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
3652
}
3753

54+
public SortEnum CurrentSort
55+
{
56+
get => _currentSort;
57+
set => SetValue(ref _currentSort, value);
58+
}
59+
3860
// I hate you today WPF
3961
public EntityTreeViewModel() : this(null)
4062
{
@@ -43,6 +65,11 @@ public EntityTreeViewModel() : this(null)
4365
public EntityTreeViewModel(UserControl control)
4466
{
4567
_control = control;
68+
var comparer = new EntityViewModel.Comparer(_currentSort);
69+
SortedGrids = new SortedView<GridViewModel>(Grids.Values, comparer);
70+
SortedCharacters = new SortedView<CharacterViewModel>(Characters.Values, comparer);
71+
SortedFloatingObjects = new SortedView<EntityViewModel>(FloatingObjects.Values, comparer);
72+
SortedVoxelMaps = new SortedView<VoxelMapViewModel>(VoxelMaps.Values, comparer);
4673
}
4774

4875
public void Init()
@@ -85,16 +112,16 @@ private void MyEntities_OnEntityAdd(VRage.Game.Entity.MyEntity obj)
85112
switch (obj)
86113
{
87114
case MyCubeGrid grid:
88-
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
115+
Grids.Add(grid.EntityId, new GridViewModel(grid, this));
89116
break;
90117
case MyCharacter character:
91-
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
118+
Characters.Add(character.EntityId, new CharacterViewModel(character, this));
92119
break;
93120
case MyFloatingObject floating:
94-
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
121+
FloatingObjects.Add(floating.EntityId, new FloatingObjectViewModel(floating, this));
95122
break;
96123
case MyVoxelBase voxel:
97-
VoxelMaps.Add(obj.EntityId, new VoxelMapViewModel(voxel, this));
124+
VoxelMaps.Add(voxel.EntityId, new VoxelMapViewModel(voxel, this));
98125
break;
99126
}
100127
}

‎Torch.Server/Views/EntitiesControl.xaml‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
<Grid Grid.Column="0">
2323
<Grid.RowDefinitions>
2424
<RowDefinition />
25+
<RowDefinition Height="Auto"/>
2526
<RowDefinition Height="Auto" />
2627
</Grid.RowDefinitions>
2728
<TreeView Grid.Row="0" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged"
28-
TreeViewItem.Expanded="TreeViewItem_OnExpanded">
29+
TreeViewItem.Expanded="TreeViewItem_OnExpanded" Name="EntityTree">
2930
<TreeView.Resources>
3031
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}"
3132
ItemsSource="{Binding Path=Blocks}">
@@ -46,46 +47,47 @@
4647
</HierarchicalDataTemplate>
4748
<HierarchicalDataTemplate DataType="{x:Type entities:VoxelMapViewModel}"
4849
ItemsSource="{Binding AttachedGrids}">
49-
<TextBlock Text="{Binding Name}" />
50+
<TextBlock Text="{Binding DescriptiveName}" />
5051
</HierarchicalDataTemplate>
5152
</TreeView.Resources>
52-
<TreeViewItem ItemsSource="{Binding Path=Grids.Values}">
53+
<TreeViewItem ItemsSource="{Binding Path=SortedGrids}">
5354
<TreeViewItem.Header>
54-
<TextBlock Text="{Binding Grids.Count, StringFormat=Grids ({0})}" />
55+
<TextBlock Text="{Binding SortedGrids.Count, StringFormat=Grids ({0})}" />
5556
</TreeViewItem.Header>
5657
</TreeViewItem>
57-
<TreeViewItem ItemsSource="{Binding Characters.Values}">
58+
<TreeViewItem ItemsSource="{Binding SortedCharacters}">
5859
<TreeViewItem.Header>
59-
<TextBlock Text="{Binding Characters.Count, StringFormat=Characters ({0})}" />
60+
<TextBlock Text="{Binding SortedCharacters.Count, StringFormat=Characters ({0})}" />
6061
</TreeViewItem.Header>
6162
<TreeViewItem.ItemTemplate>
6263
<DataTemplate>
63-
<TextBlock Text="{Binding Name}" />
64+
<TextBlock Text="{Binding DescriptiveName}" />
6465
</DataTemplate>
6566
</TreeViewItem.ItemTemplate>
6667
</TreeViewItem>
67-
<TreeViewItem ItemsSource="{Binding VoxelMaps.Values}">
68+
<TreeViewItem ItemsSource="{Binding SortedVoxelMaps}">
6869
<TreeViewItem.Header>
69-
<TextBlock Text="{Binding VoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
70+
<TextBlock Text="{Binding SortedVoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
7071
</TreeViewItem.Header>
7172
<TreeViewItem.ItemTemplate>
7273
<DataTemplate>
73-
<TextBlock Text="{Binding Name}" />
74+
<TextBlock Text="{Binding DescriptiveName}" />
7475
</DataTemplate>
7576
</TreeViewItem.ItemTemplate>
7677
</TreeViewItem>
77-
<TreeViewItem ItemsSource="{Binding FloatingObjects.Values}">
78+
<TreeViewItem ItemsSource="{Binding SortedFloatingObjects}">
7879
<TreeViewItem.Header>
79-
<TextBlock Text="{Binding FloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
80+
<TextBlock Text="{Binding SortedFloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
8081
</TreeViewItem.Header>
8182
<TreeViewItem.ItemTemplate>
8283
<DataTemplate>
83-
<TextBlock Text="{Binding Name}" />
84+
<TextBlock Text="{Binding DescriptiveName}" />
8485
</DataTemplate>
8586
</TreeViewItem.ItemTemplate>
8687
</TreeViewItem>
8788
</TreeView>
88-
<StackPanel Grid.Row="1" DockPanel.Dock="Bottom">
89+
<ComboBox Grid.Row="1" Margin="3" Name="SortCombo" SelectionChanged="SortCombo_SelectionChanged"/>
90+
<StackPanel Grid.Row="2" DockPanel.Dock="Bottom">
8991
<Button Content="Delete" Click="Delete_OnClick" IsEnabled="{Binding CurrentEntity.CanDelete}"
9092
Margin="3" />
9193
<Button Content="Stop" Click="Stop_OnClick" IsEnabled="{Binding CurrentEntity.CanStop}" Margin="3" />

‎Torch.Server/Views/EntitiesControl.xaml.cs‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public EntitiesControl()
3535
Entities = new EntityTreeViewModel(this);
3636
DataContext = Entities;
3737
Entities.Init();
38+
SortCombo.ItemsSource = Enum.GetNames(typeof(EntityTreeViewModel.SortEnum));
3839
}
3940

4041
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
@@ -77,5 +78,30 @@ private void TreeViewItem_OnExpanded(object sender, RoutedEventArgs e)
7778
if (item.DataContext is ILazyLoad l)
7879
l.Load();
7980
}
81+
82+
private void SortCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
83+
{
84+
var sort = (EntityTreeViewModel.SortEnum)SortCombo.SelectedIndex;
85+
86+
var comparer = new EntityViewModel.Comparer(sort);
87+
88+
Task[] sortTasks = new Task[4];
89+
90+
Entities.CurrentSort = sort;
91+
Entities.SortedCharacters.Sort(comparer);
92+
Entities.SortedFloatingObjects.Sort(comparer);
93+
Entities.SortedGrids.Sort(comparer);
94+
Entities.SortedVoxelMaps.Sort(comparer);
95+
96+
foreach (var i in Entities.SortedCharacters)
97+
i.DescriptiveName = i.GetSortedName(sort);
98+
foreach (var i in Entities.SortedFloatingObjects)
99+
i.DescriptiveName = i.GetSortedName(sort);
100+
foreach (var i in Entities.SortedGrids)
101+
i.DescriptiveName = i.GetSortedName(sort);
102+
foreach (var i in Entities.SortedVoxelMaps)
103+
i.DescriptiveName = i.GetSortedName(sort);
104+
105+
}
80106
}
81107
}

‎Torch/Collections/MtObservableList.cs‎

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Torch.Collections
1313
/// Multithread safe, observable list
1414
/// </summary>
1515
/// <typeparam name="T">Value type</typeparam>
16-
public class MtObservableList<T> : MtObservableCollection<IList<T>, T>, IList<T>, IList
16+
public class MtObservableList<T> : MtObservableCollection<List<T>, T>, IList<T>, IList
1717
{
1818
/// <summary>
1919
/// Initializes a new instance of the MtObservableList class that is empty and has the default initial capacity.
@@ -114,16 +114,34 @@ public void Sort<TKey>(Func<T, TKey> selector, IComparer<TKey> comparer = null)
114114
using (Lock.WriteUsing())
115115
{
116116
comparer = comparer ?? Comparer<TKey>.Default;
117-
if (Backing is List<T> lst)
118-
lst.Sort(new TransformComparer<T, TKey>(selector, comparer));
119-
else
120-
{
121-
List<T> sortedItems = Backing.OrderBy(selector, comparer).ToList();
122-
Backing.Clear();
123-
foreach (T v in sortedItems)
124-
Backing.Add(v);
125-
}
117+
Backing.Sort(new TransformComparer<T, TKey>(selector, comparer));
118+
}
119+
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move));
120+
}
121+
122+
/// <summary>
123+
/// Sorts the list using the given comparer./>
124+
/// </summary>
125+
public void Sort(IComparer<T> comparer)
126+
{
127+
using (DeferredUpdate())
128+
using (Lock.WriteUsing())
129+
{
130+
Backing.Sort(comparer);
126131
}
132+
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move));
133+
}
134+
135+
/// <summary>
136+
/// Searches the entire list for an element using the specified comparer and returns the zero-based index of the element.
137+
/// </summary>
138+
/// <param name="item"></param>
139+
/// <param name="comparer"></param>
140+
/// <returns></returns>
141+
public int BinarySearch(T item, IComparer<T> comparer = null)
142+
{
143+
using(Lock.ReadUsing())
144+
return Backing.BinarySearch(item, comparer ?? Comparer<T>.Default);
127145
}
128146

129147
/// <inheritdoc/>

0 commit comments

Comments
 (0)