Browse Source

Delete PixiEditorPrototype project

Equbuxu 3 years ago
parent
commit
f0e69796a8
52 changed files with 0 additions and 5725 deletions
  1. 0 32
      src/PixiEditor.sln
  2. 0 9
      src/PixiEditorPrototype/App.xaml
  3. 0 10
      src/PixiEditorPrototype/App.xaml.cs
  4. 0 10
      src/PixiEditorPrototype/AssemblyInfo.cs
  5. 0 107
      src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs
  6. 0 21
      src/PixiEditorPrototype/Converters/BlendModeToStringConverter.cs
  7. 0 20
      src/PixiEditorPrototype/Converters/BoolToVisibilityConverter.cs
  8. 0 25
      src/PixiEditorPrototype/Converters/ScaleToBitmapScalingModeConverter.cs
  9. 0 89
      src/PixiEditorPrototype/CustomControls/BlendModeComboBox.cs
  10. 0 90
      src/PixiEditorPrototype/CustomControls/SelectionOverlay.cs
  11. 0 4
      src/PixiEditorPrototype/CustomControls/SymmetryOverlay/SymmetryAxisDragInfo.cs
  12. 0 220
      src/PixiEditorPrototype/CustomControls/SymmetryOverlay/SymmetryOverlay.cs
  13. 0 8
      src/PixiEditorPrototype/CustomControls/TransformOverlay/Anchor.cs
  14. 0 8
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformCornerFreedom.cs
  15. 0 254
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformHelper.cs
  16. 0 324
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformOverlay.cs
  17. 0 9
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformSideFreedom.cs
  18. 0 10
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformState.cs
  19. 0 160
      src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformUpdateHelper.cs
  20. 0 1
      src/PixiEditorPrototype/GlobalUsings.cs
  21. 0 42
      src/PixiEditorPrototype/Helpers/SurfaceHelpers.cs
  22. 0 190
      src/PixiEditorPrototype/Models/ActionAccumulator.cs
  23. 0 30
      src/PixiEditorPrototype/Models/BlendModeEx.cs
  24. 0 21
      src/PixiEditorPrototype/Models/DocumentHelpers.cs
  25. 0 9
      src/PixiEditorPrototype/Models/DocumentState.cs
  26. 0 144
      src/PixiEditorPrototype/Models/DocumentStructureHelper.cs
  27. 0 319
      src/PixiEditorPrototype/Models/DocumentUpdater.cs
  28. 0 18
      src/PixiEditorPrototype/Models/IReadOnlyListEx.cs
  29. 0 6
      src/PixiEditorPrototype/Models/RefreshViewport_PassthroughAction.cs
  30. 0 6
      src/PixiEditorPrototype/Models/RemoveViewport_PassthroughAction.cs
  31. 0 226
      src/PixiEditorPrototype/Models/Rendering/AffectedChunkGatherer.cs
  32. 0 3
      src/PixiEditorPrototype/Models/Rendering/RenderInfos/CanvasPreviewDirty_RenderInfo.cs
  33. 0 5
      src/PixiEditorPrototype/Models/Rendering/RenderInfos/DirtyRect_RenderInfo.cs
  34. 0 5
      src/PixiEditorPrototype/Models/Rendering/RenderInfos/IRenderInfo.cs
  35. 0 5
      src/PixiEditorPrototype/Models/Rendering/RenderInfos/MaskPreviewDirty_RenderInfo.cs
  36. 0 5
      src/PixiEditorPrototype/Models/Rendering/RenderInfos/PreviewDirty_RenderInfo.cs
  37. 0 303
      src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs
  38. 0 22
      src/PixiEditorPrototype/Models/Tool.cs
  39. 0 13
      src/PixiEditorPrototype/Models/ViewportInfo.cs
  40. 0 32
      src/PixiEditorPrototype/PixiEditorPrototype.csproj
  41. 0 38
      src/PixiEditorPrototype/RelayCommand.cs
  42. 0 42
      src/PixiEditorPrototype/ReverseOrderStackPanel.cs
  43. 0 136
      src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml
  44. 0 257
      src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml.cs
  45. 0 98
      src/PixiEditorPrototype/ViewModels/DocumentTransformViewModel.cs
  46. 0 901
      src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs
  47. 0 11
      src/PixiEditorPrototype/ViewModels/FolderViewModel.cs
  48. 0 22
      src/PixiEditorPrototype/ViewModels/LayerViewModel.cs
  49. 0 174
      src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs
  50. 0 367
      src/PixiEditorPrototype/ViewModels/ViewModelMain.cs
  51. 0 853
      src/PixiEditorPrototype/Views/MainWindow.xaml
  52. 0 11
      src/PixiEditorPrototype/Views/MainWindow.xaml.cs

+ 0 - 32
src/PixiEditor.sln

@@ -32,8 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.ChangeableDocume
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditor.Zoombox", "PixiEditor.Zoombox\PixiEditor.Zoombox.csproj", "{69DD5830-C682-49FB-B1A5-D2A506EEA06B}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PixiEditorPrototype", "PixiEditorPrototype\PixiEditorPrototype.csproj", "{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}"
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -394,36 +392,6 @@ Global
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x64.Build.0 = Release|Any CPU
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x86.ActiveCfg = Release|Any CPU
 		{69DD5830-C682-49FB-B1A5-D2A506EEA06B}.Release|x86.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|x64.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Debug|x86.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|Any CPU.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|Any CPU.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|x64.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|x64.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|x86.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Dev Release|x86.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|Any CPU.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|x64.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|x64.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|x86.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX Debug|x86.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|Any CPU.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|Any CPU.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|x64.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|x64.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|x86.ActiveCfg = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.MSIX|x86.Build.0 = Debug|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|Any CPU.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|x64.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|x64.Build.0 = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|x86.ActiveCfg = Release|Any CPU
-		{C0D2FF8D-DACB-4D03-BBAE-43CE5D21E5EC}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 0 - 9
src/PixiEditorPrototype/App.xaml

@@ -1,9 +0,0 @@
-<Application x:Class="PixiEditorPrototype.App"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:local="clr-namespace:PixiEditorPrototype"
-             StartupUri="Views/MainWindow.xaml">
-    <Application.Resources>
-
-    </Application.Resources>
-</Application>

+ 0 - 10
src/PixiEditorPrototype/App.xaml.cs

@@ -1,10 +0,0 @@
-using System.Windows;
-
-namespace PixiEditorPrototype;
-
-/// <summary>
-/// Interaction logic for App.xaml
-/// </summary>
-public partial class App : Application
-{
-}

+ 0 - 10
src/PixiEditorPrototype/AssemblyInfo.cs

@@ -1,10 +0,0 @@
-using System.Windows;
-
-[assembly: ThemeInfo(
-    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
-                                     //(used if a resource is not found in the page,
-                                     // or application resource dictionaries)
-    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
-                                              //(used if a resource is not found in the page,
-                                              // app, or any theme specific resource dictionaries)
-)]

+ 0 - 107
src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs

@@ -1,107 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using System.Windows.Input;
-using Microsoft.Xaml.Behaviors;
-
-namespace PixiEditorPrototype.Behaviors;
-
-internal class SliderUpdateBehavior : Behavior<Slider>
-{
-    public static DependencyProperty DragValueChangedProperty = DependencyProperty.Register(nameof(DragValueChanged), typeof(ICommand), typeof(SliderUpdateBehavior));
-    public ICommand? DragValueChanged
-    {
-        get => (ICommand)GetValue(DragValueChangedProperty);
-        set => SetValue(DragValueChangedProperty, value);
-    }
-    public static DependencyProperty DragEndedProperty = DependencyProperty.Register(nameof(DragEnded), typeof(ICommand), typeof(SliderUpdateBehavior));
-    public ICommand? DragEnded
-    {
-        get => (ICommand)GetValue(DragEndedProperty);
-        set => SetValue(DragEndedProperty, value);
-    }
-
-    public static DependencyProperty ValueFromSliderProperty =
-        DependencyProperty.Register(nameof(ValueFromSlider), typeof(double), typeof(SliderUpdateBehavior), new(OnSliderValuePropertyChange));
-    public double ValueFromSlider
-    {
-        get => (double)GetValue(ValueFromSliderProperty);
-        set => SetValue(ValueFromSliderProperty, value);
-    }
-
-    private bool attached = false;
-    private bool dragging = false;
-    private bool valueChangedWhileDragging = false;
-    protected override void OnAttached()
-    {
-        AssociatedObject.Loaded += AssociatedObject_Loaded;
-        AssociatedObject.Focusable = false;
-
-
-        if (AssociatedObject.IsLoaded)
-            AttachEvents();
-    }
-
-    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
-    {
-        AttachEvents();
-    }
-
-    private void AttachEvents()
-    {
-        if (attached)
-            return;
-        attached = true;
-        Thumb? thumb = GetThumb(AssociatedObject);
-        if (thumb is null)
-            return;
-
-        thumb.DragStarted += Thumb_DragStarted;
-        thumb.DragCompleted += Thumb_DragCompleted;
-    }
-
-    protected override void OnDetaching()
-    {
-        AssociatedObject.Loaded -= AssociatedObject_Loaded;
-        if (!attached)
-            return;
-        Thumb? thumb = GetThumb(AssociatedObject);
-        if (thumb is null)
-            return;
-
-        thumb.DragStarted -= Thumb_DragStarted;
-        thumb.DragCompleted -= Thumb_DragCompleted;
-    }
-
-    private static void OnSliderValuePropertyChange(DependencyObject slider, DependencyPropertyChangedEventArgs e)
-    {
-        SliderUpdateBehavior? obj = (SliderUpdateBehavior)slider;
-        if (obj.dragging)
-        {
-            if (obj.DragValueChanged is not null && obj.DragValueChanged.CanExecute(e.NewValue))
-                obj.DragValueChanged.Execute(e.NewValue);
-            obj.valueChangedWhileDragging = true;
-        }
-    }
-
-    private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
-    {
-        dragging = false;
-        if (valueChangedWhileDragging && DragEnded is not null && DragEnded.CanExecute(null))
-            DragEnded.Execute(null);
-        valueChangedWhileDragging = false;
-    }
-
-    private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
-    {
-        dragging = true;
-    }
-
-    private static Thumb? GetThumb(Slider slider)
-    {
-        Track? track = slider.Template.FindName("PART_Track", slider) as Track;
-        return track is null ? null : track.Thumb;
-    }
-
-
-}

+ 0 - 21
src/PixiEditorPrototype/Converters/BlendModeToStringConverter.cs

@@ -1,21 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows.Data;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditorPrototype.Models;
-
-namespace PixiEditorPrototype.Converters;
-internal class BlendModeToStringConverter : IValueConverter
-{
-    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-    {
-        if (value is not BlendMode mode)
-            return "<null>";
-        return mode.EnglishName();
-    }
-
-    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-    {
-        throw new NotImplementedException();
-    }
-}

+ 0 - 20
src/PixiEditorPrototype/Converters/BoolToVisibilityConverter.cs

@@ -1,20 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows;
-using System.Windows.Data;
-
-namespace PixiEditorPrototype.Converters;
-
-internal class BoolToVisibilityConverter : IValueConverter
-{
-    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-    {
-        bool boolean = (bool)value;
-        return boolean ? Visibility.Visible : Visibility.Collapsed;
-    }
-
-    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-    {
-        throw new NotImplementedException();
-    }
-}

+ 0 - 25
src/PixiEditorPrototype/Converters/ScaleToBitmapScalingModeConverter.cs

@@ -1,25 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows;
-using System.Windows.Data;
-using System.Windows.Media;
-
-namespace PixiEditorPrototype.Converters
-{
-    internal class ScaleToBitmapScalingModeConverter : IValueConverter
-    {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (value is not double scale)
-                return DependencyProperty.UnsetValue;
-            if (scale < 1)
-                return BitmapScalingMode.HighQuality;
-            return BitmapScalingMode.NearestNeighbor;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
-    }
-}

+ 0 - 89
src/PixiEditorPrototype/CustomControls/BlendModeComboBox.cs

@@ -1,89 +0,0 @@
-using System.Collections.Generic;
-using System.Windows;
-using System.Windows.Controls;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditorPrototype.Models;
-
-namespace PixiEditorPrototype.CustomControls;
-internal class BlendModeComboBox : ComboBox
-{
-    public static readonly DependencyProperty SelectedBlendModeProperty = DependencyProperty.Register(
-        nameof(SelectedBlendMode), typeof(BlendMode), typeof(BlendModeComboBox),
-        new PropertyMetadata(BlendMode.Normal, OnBlendModeChange));
-
-    public BlendMode SelectedBlendMode
-    {
-        get { return (BlendMode)GetValue(SelectedBlendModeProperty); }
-        set { SetValue(SelectedBlendModeProperty, value); }
-    }
-
-    private bool ignoreDepPropChange = false;
-    private bool ignoreSelectionChange = false;
-
-    public BlendModeComboBox()
-    {
-        AddItems();
-        SelectionChanged += OnSelectionChange;
-    }
-
-    private void OnSelectionChange(object sender, SelectionChangedEventArgs e)
-    {
-        if (ignoreSelectionChange || e.AddedItems.Count == 0 || e.AddedItems[0] is not ComboBoxItem item || item.Tag is not BlendMode mode)
-            return;
-        ignoreDepPropChange = true;
-        SelectedBlendMode = mode;
-        ignoreDepPropChange = false;
-    }
-
-    private static void OnBlendModeChange(DependencyObject obj, DependencyPropertyChangedEventArgs args)
-    {
-        var combobox = (BlendModeComboBox)obj;
-        if (combobox.ignoreDepPropChange)
-            return;
-        foreach (var item in combobox.Items)
-        {
-            if (item is not ComboBoxItem cbItem)
-                continue;
-            if ((BlendMode)cbItem.Tag == (BlendMode)args.NewValue)
-            {
-                combobox.ignoreSelectionChange = true;
-                combobox.SelectedItem = item;
-                combobox.ignoreSelectionChange = false;
-                break;
-            }
-        }
-    }
-
-    private void AddItems()
-    {
-        var items = new List<UIElement>() {
-            new ComboBoxItem() { Content = BlendMode.Normal.EnglishName(), Tag = BlendMode.Normal },
-            new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Darken.EnglishName(), Tag = BlendMode.Darken },
-            new ComboBoxItem() { Content = BlendMode.Multiply.EnglishName(), Tag = BlendMode.Multiply },
-            new ComboBoxItem() { Content = BlendMode.ColorBurn.EnglishName(), Tag = BlendMode.ColorBurn },
-            new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Lighten.EnglishName(), Tag = BlendMode.Lighten },
-            new ComboBoxItem() { Content = BlendMode.Screen.EnglishName(), Tag = BlendMode.Screen },
-            new ComboBoxItem() { Content = BlendMode.ColorDodge.EnglishName(), Tag = BlendMode.ColorDodge },
-            new ComboBoxItem() { Content = BlendMode.LinearDodge.EnglishName(), Tag = BlendMode.LinearDodge },
-            new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Overlay.EnglishName(), Tag = BlendMode.Overlay },
-            new ComboBoxItem() { Content = BlendMode.SoftLight.EnglishName(), Tag = BlendMode.SoftLight },
-            new ComboBoxItem() { Content = BlendMode.HardLight.EnglishName(), Tag = BlendMode.HardLight },
-            new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Difference.EnglishName(), Tag = BlendMode.Difference },
-            new ComboBoxItem() { Content = BlendMode.Exclusion.EnglishName(), Tag = BlendMode.Exclusion },
-            new Separator(),
-            new ComboBoxItem() { Content = BlendMode.Hue.EnglishName(), Tag = BlendMode.Hue },
-            new ComboBoxItem() { Content = BlendMode.Saturation.EnglishName(), Tag = BlendMode.Saturation },
-            new ComboBoxItem() { Content = BlendMode.Luminosity.EnglishName(), Tag = BlendMode.Luminosity },
-            new ComboBoxItem() { Content = BlendMode.Color.EnglishName(), Tag = BlendMode.Color }
-        };
-        foreach (var item in items)
-        {
-            Items.Add(item);
-        }
-        SelectedIndex = 0;
-    }
-}

+ 0 - 90
src/PixiEditorPrototype/CustomControls/SelectionOverlay.cs

@@ -1,90 +0,0 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-using System.Windows.Media.Animation;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.CustomControls;
-
-internal class SelectionOverlay : Control
-{
-    public static readonly DependencyProperty PathProperty =
-        DependencyProperty.Register(nameof(Path), typeof(SKPath), typeof(SelectionOverlay),
-            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public static readonly DependencyProperty ZoomboxScaleProperty =
-        DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(SelectionOverlay), new(1.0, OnZoomboxScaleChanged));
-
-    public double ZoomboxScale
-    {
-        get => (double)GetValue(ZoomboxScaleProperty);
-        set => SetValue(ZoomboxScaleProperty, value);
-    }
-
-    public SKPath? Path
-    {
-        get => (SKPath?)GetValue(PathProperty);
-        set => SetValue(PathProperty, value);
-    }
-
-    private Pen whitePen = new Pen(Brushes.White, 1);
-    private Pen blackDashedPen = new Pen(Brushes.Black, 1) { DashStyle = frame7 };
-    private Brush fillBrush = new SolidColorBrush(Color.FromArgb(80, 0, 80, 255));
-
-    private static DashStyle frame1 = new DashStyle(new double[] { 2, 4 }, 0);
-    private static DashStyle frame2 = new DashStyle(new double[] { 2, 4 }, 1);
-    private static DashStyle frame3 = new DashStyle(new double[] { 2, 4 }, 2);
-    private static DashStyle frame4 = new DashStyle(new double[] { 2, 4 }, 3);
-    private static DashStyle frame5 = new DashStyle(new double[] { 2, 4 }, 4);
-    private static DashStyle frame6 = new DashStyle(new double[] { 2, 4 }, 5);
-    private static DashStyle frame7 = new DashStyle(new double[] { 2, 4 }, 6);
-
-    private Geometry renderPath = new PathGeometry();
-    private PathFigureCollectionConverter converter = new();
-
-    public SelectionOverlay()
-    {
-        IsHitTestVisible = false;
-
-        blackDashedPen.BeginAnimation(Pen.DashStyleProperty, new ObjectAnimationUsingKeyFrames()
-        {
-            KeyFrames = new ObjectKeyFrameCollection()
-            {
-                new DiscreteObjectKeyFrame(frame1, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame2, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame3, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame4, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame5, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame6, KeyTime.Paced),
-                new DiscreteObjectKeyFrame(frame7, KeyTime.Paced),
-            },
-            RepeatBehavior = RepeatBehavior.Forever,
-            Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500))
-        });
-
-    }
-
-    protected override void OnRender(DrawingContext drawingContext)
-    {
-        base.OnRender(drawingContext);
-        if (Path is null)
-            return;
-
-        renderPath = new PathGeometry()
-        {
-            FillRule = FillRule.EvenOdd,
-            Figures = (PathFigureCollection?)converter.ConvertFromString(Path.ToSvgPathData()),
-        };
-        drawingContext.DrawGeometry(null, whitePen, renderPath);
-        drawingContext.DrawGeometry(fillBrush, blackDashedPen, renderPath);
-    }
-
-    private static void OnZoomboxScaleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
-    {
-        var self = (SelectionOverlay)obj;
-        double newScale = (double)args.NewValue;
-        self.whitePen.Thickness = 1.0 / newScale;
-        self.blackDashedPen.Thickness = 1.0 / newScale;
-    }
-}

+ 0 - 4
src/PixiEditorPrototype/CustomControls/SymmetryOverlay/SymmetryAxisDragInfo.cs

@@ -1,4 +0,0 @@
-using PixiEditor.ChangeableDocument.Enums;
-
-namespace PixiEditorPrototype.CustomControls.SymmetryOverlay;
-internal record class SymmetryAxisDragInfo(SymmetryAxisDirection Direction, int NewPosition);

+ 0 - 220
src/PixiEditorPrototype/CustomControls/SymmetryOverlay/SymmetryOverlay.cs

@@ -1,220 +0,0 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Media;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Enums;
-
-namespace PixiEditorPrototype.CustomControls.SymmetryOverlay;
-
-internal class SymmetryOverlay : Control
-{
-    public static readonly DependencyProperty HorizontalAxisYProperty =
-        DependencyProperty.Register(nameof(HorizontalAxisY), typeof(int), typeof(SymmetryOverlay),
-            new(0, OnPositionUpdate));
-
-    public int HorizontalAxisY
-    {
-        get => (int)GetValue(HorizontalAxisYProperty);
-        set => SetValue(HorizontalAxisYProperty, value);
-    }
-
-    public static readonly DependencyProperty VerticalAxisXProperty =
-        DependencyProperty.Register(nameof(VerticalAxisX), typeof(int), typeof(SymmetryOverlay),
-            new(0, OnPositionUpdate));
-
-    public int VerticalAxisX
-    {
-        get => (int)GetValue(VerticalAxisXProperty);
-        set => SetValue(VerticalAxisXProperty, value);
-    }
-
-    public static readonly DependencyProperty HorizontalAxisVisibleProperty =
-        DependencyProperty.Register(nameof(HorizontalAxisVisible), typeof(bool), typeof(SymmetryOverlay),
-            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public bool HorizontalAxisVisible
-    {
-        get => (bool)GetValue(HorizontalAxisVisibleProperty);
-        set => SetValue(HorizontalAxisVisibleProperty, value);
-    }
-
-    public static readonly DependencyProperty VerticalAxisVisibleProperty =
-        DependencyProperty.Register(nameof(VerticalAxisVisible), typeof(bool), typeof(SymmetryOverlay),
-            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public bool VerticalAxisVisible
-    {
-        get => (bool)GetValue(VerticalAxisVisibleProperty);
-        set => SetValue(VerticalAxisVisibleProperty, value);
-    }
-
-    public static readonly DependencyProperty ZoomboxScaleProperty =
-        DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(SymmetryOverlay),
-            new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public double ZoomboxScale
-    {
-        get => (double)GetValue(ZoomboxScaleProperty);
-        set => SetValue(ZoomboxScaleProperty, value);
-    }
-
-    public static readonly DependencyProperty DragCommandProperty =
-        DependencyProperty.Register(nameof(DragCommand), typeof(ICommand), typeof(SymmetryOverlay), new(null));
-
-    public ICommand? DragCommand
-    {
-        get => (ICommand)GetValue(DragCommandProperty);
-        set => SetValue(DragCommandProperty, value);
-    }
-
-    public static readonly DependencyProperty DragEndCommandProperty =
-        DependencyProperty.Register(nameof(DragEndCommand), typeof(ICommand), typeof(SymmetryOverlay), new(null));
-
-    public ICommand? DragEndCommand
-    {
-        get => (ICommand)GetValue(DragEndCommandProperty);
-        set => SetValue(DragEndCommandProperty, value);
-    }
-
-    private const double HandleSize = 16;
-    private PathGeometry handleGeometry = new()
-    {
-        FillRule = FillRule.Nonzero,
-        Figures = (PathFigureCollection?)new PathFigureCollectionConverter()
-            .ConvertFrom($"M -1 -0.5 L -0.5 -0.5 L 0 0 L -0.5 0.5 L -1 0.5 Z"),
-    };
-
-    private Pen borderPen = new Pen(Brushes.Black, 1.0);
-    private int horizontalAxisY;
-    private int verticalAxisX;
-
-    protected override void OnRender(DrawingContext drawingContext)
-    {
-        base.OnRender(drawingContext);
-        if (!HorizontalAxisVisible && !VerticalAxisVisible)
-            return;
-
-        borderPen.Thickness = 1.0 / ZoomboxScale;
-        handleGeometry.Transform = new ScaleTransform(HandleSize / ZoomboxScale, HandleSize / ZoomboxScale);
-
-        if (HorizontalAxisVisible)
-        {
-            drawingContext.PushTransform(new TranslateTransform(0, horizontalAxisY));
-            drawingContext.DrawGeometry(Brushes.White, borderPen, handleGeometry);
-            drawingContext.PushTransform(new RotateTransform(180, ActualWidth / 2, 0));
-            drawingContext.DrawGeometry(Brushes.White, borderPen, handleGeometry);
-            drawingContext.Pop();
-            drawingContext.Pop();
-            drawingContext.DrawLine(borderPen, new(0, horizontalAxisY), new(ActualWidth, horizontalAxisY));
-        }
-        if (VerticalAxisVisible)
-        {
-            drawingContext.PushTransform(new RotateTransform(90));
-            drawingContext.PushTransform(new TranslateTransform(0, -verticalAxisX));
-            drawingContext.DrawGeometry(Brushes.White, borderPen, handleGeometry);
-            drawingContext.PushTransform(new RotateTransform(180, ActualHeight / 2, 0));
-            drawingContext.DrawGeometry(Brushes.White, borderPen, handleGeometry);
-            drawingContext.Pop();
-            drawingContext.Pop();
-            drawingContext.Pop();
-            drawingContext.DrawLine(borderPen, new(verticalAxisX, 0), new(verticalAxisX, ActualHeight));
-        }
-    }
-
-    protected override HitTestResult? HitTestCore(PointHitTestParameters hitTestParameters)
-    {
-        // prevent the line from blocking mouse input
-        var point = hitTestParameters.HitPoint;
-        if (point.X > 0 && point.Y > 0 && point.X < ActualWidth && point.Y < ActualHeight)
-            return null;
-        return new PointHitTestResult(this, hitTestParameters.HitPoint);
-    }
-
-    private SymmetryAxisDirection? IsTouchingHandle(VecD position)
-    {
-        double radius = HandleSize / ZoomboxScale / 2;
-        VecD left = new(-radius, horizontalAxisY);
-        VecD right = new(ActualWidth + radius, horizontalAxisY);
-        VecD up = new(verticalAxisX, -radius);
-        VecD down = new(verticalAxisX, ActualHeight + radius);
-
-        if (HorizontalAxisVisible && (Math.Abs((left - position).LongestAxis) < radius || Math.Abs((right - position).LongestAxis) < radius))
-            return SymmetryAxisDirection.Horizontal;
-        if (VerticalAxisVisible && (Math.Abs((up - position).LongestAxis) < radius || Math.Abs((down - position).LongestAxis) < radius))
-            return SymmetryAxisDirection.Vertical;
-        return null;
-    }
-
-    private VecD ToVecD(Point pos) => new VecD(pos.X, pos.Y);
-
-    public SymmetryAxisDirection? capturedDirection;
-
-    protected override void OnMouseDown(MouseButtonEventArgs e)
-    {
-        base.OnMouseDown(e);
-
-        var pos = ToVecD(e.GetPosition(this));
-        var dir = IsTouchingHandle(pos);
-        if (dir is null)
-            return;
-        capturedDirection = dir.Value;
-        CaptureMouse();
-        e.Handled = true;
-    }
-
-    private void CallSymmetryDragCommand(SymmetryAxisDirection direction, int position)
-    {
-        SymmetryAxisDragInfo dragInfo = new(direction, position);
-        if (DragCommand is not null && DragCommand.CanExecute(dragInfo))
-            DragCommand.Execute(dragInfo);
-    }
-    private void CallSymmetryDragEndCommand(SymmetryAxisDirection direction)
-    {
-        if (DragEndCommand is not null && DragEndCommand.CanExecute(direction))
-            DragEndCommand.Execute(direction);
-    }
-
-    protected override void OnMouseUp(MouseButtonEventArgs e)
-    {
-        base.OnMouseUp(e);
-
-        if (capturedDirection is null)
-            return;
-        ReleaseMouseCapture();
-
-        CallSymmetryDragEndCommand((SymmetryAxisDirection)capturedDirection);
-
-        capturedDirection = null;
-        e.Handled = true;
-    }
-
-    protected override void OnMouseMove(MouseEventArgs e)
-    {
-        base.OnMouseMove(e);
-
-        if (capturedDirection is null)
-            return;
-        var pos = ToVecD(e.GetPosition(this));
-        if (capturedDirection == SymmetryAxisDirection.Horizontal)
-        {
-            horizontalAxisY = (int)Math.Round(Math.Clamp(pos.Y, 0, ActualHeight));
-            CallSymmetryDragCommand((SymmetryAxisDirection)capturedDirection, horizontalAxisY);
-        }
-        else if (capturedDirection == SymmetryAxisDirection.Vertical)
-        {
-            verticalAxisX = (int)Math.Round(Math.Clamp(pos.X, 0, ActualWidth));
-            CallSymmetryDragCommand((SymmetryAxisDirection)capturedDirection, verticalAxisX);
-        }
-        e.Handled = true;
-    }
-
-    private static void OnPositionUpdate(DependencyObject obj, DependencyPropertyChangedEventArgs args)
-    {
-        var self = (SymmetryOverlay)obj;
-        self.horizontalAxisY = self.HorizontalAxisY;
-        self.verticalAxisX = self.VerticalAxisX;
-        self.InvalidateVisual();
-    }
-}

+ 0 - 8
src/PixiEditorPrototype/CustomControls/TransformOverlay/Anchor.cs

@@ -1,8 +0,0 @@
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-
-internal enum Anchor
-{
-    TopLeft, TopRight, BottomLeft, BottomRight,
-    Top, Left, Right, Bottom,
-    Origin
-}

+ 0 - 8
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformCornerFreedom.cs

@@ -1,8 +0,0 @@
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-internal enum TransformCornerFreedom
-{
-    Locked,
-    ScaleProportionally,
-    Scale,
-    Free
-}

+ 0 - 254
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformHelper.cs

@@ -1,254 +0,0 @@
-using System;
-using System.Windows;
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-internal static class TransformHelper
-{
-    public const double AnchorSize = 10;
-    public const double MoveHandleSize = 16;
-
-    public static Rect ToAnchorRect(VecD pos, double zoomboxScale)
-    {
-        double scaled = AnchorSize / zoomboxScale;
-        return new Rect(pos.X - scaled / 2, pos.Y - scaled / 2, scaled, scaled);
-    }
-
-    public static Rect ToHandleRect(VecD pos, double zoomboxScale)
-    {
-        double scaled = MoveHandleSize / zoomboxScale;
-        return new Rect(pos.X - scaled / 2, pos.Y - scaled / 2, scaled, scaled);
-    }
-
-    public static VecD ToVecD(Point pos) => new VecD(pos.X, pos.Y);
-    public static Point ToPoint(VecD vec) => new Point(vec.X, vec.Y);
-
-    public static ShapeCorners SnapToPixels(ShapeCorners corners)
-    {
-        corners.TopLeft = corners.TopLeft.Round();
-        corners.TopRight = corners.TopRight.Round();
-        corners.BottomLeft = corners.BottomLeft.Round();
-        corners.BottomRight = corners.BottomRight.Round();
-        return corners;
-    }
-
-    private static double GetSnappingAngle(double angle)
-    {
-        return Math.Round(angle * 8 / (Math.PI * 2)) * (Math.PI * 2) / 8;
-    }
-    public static double FindSnappingAngle(ShapeCorners corners, double desiredAngle)
-    {
-        var desTop = (corners.TopLeft - corners.TopRight).Rotate(desiredAngle).Angle;
-        var desRight = (corners.TopRight - corners.BottomRight).Rotate(desiredAngle).Angle;
-        var desBottom = (corners.BottomRight - corners.BottomLeft).Rotate(desiredAngle).Angle;
-        var desLeft = (corners.BottomLeft - corners.TopLeft).Rotate(desiredAngle).Angle;
-
-        var deltaTop = GetSnappingAngle(desTop) - desTop;
-        var deltaRight = GetSnappingAngle(desRight) - desRight;
-        var deltaLeft = GetSnappingAngle(desLeft) - desLeft;
-        var deltaBottom = GetSnappingAngle(desBottom) - desBottom;
-
-        var minDelta = deltaTop;
-        if (Math.Abs(minDelta) > Math.Abs(deltaRight))
-            minDelta = deltaRight;
-        if (Math.Abs(minDelta) > Math.Abs(deltaLeft))
-            minDelta = deltaLeft;
-        if (Math.Abs(minDelta) > Math.Abs(deltaBottom))
-            minDelta = deltaBottom;
-        return minDelta + desiredAngle;
-    }
-
-    public static VecD OriginFromCorners(ShapeCorners corners)
-    {
-        var maybeOrigin = TwoLineIntersection(
-            GetAnchorPosition(corners, Anchor.Top),
-            GetAnchorPosition(corners, Anchor.Bottom),
-            GetAnchorPosition(corners, Anchor.Left),
-            GetAnchorPosition(corners, Anchor.Right)
-            );
-        return maybeOrigin ?? corners.TopLeft.Lerp(corners.BottomRight, 0.5);
-    }
-
-    public static VecD? TwoLineIntersection(VecD line1Start, VecD line1End, VecD line2Start, VecD line2End)
-    {
-        const double epsilon = 0.0001;
-
-        VecD line1delta = line1End - line1Start;
-        VecD line2delta = line2End - line2Start;
-
-        // both lines are vertical, no intersections
-        if (Math.Abs(line1delta.X) < epsilon && Math.Abs(line2delta.X) < epsilon)
-            return null;
-
-        // y = mx + c
-        double m1 = line1delta.Y / line1delta.X;
-        double m2 = line2delta.Y / line2delta.X;
-
-        // line 1 is vertical (m1 is infinity)
-        if (Math.Abs(line1delta.X) < epsilon)
-        {
-            double c2 = line2Start.Y - line2Start.X * m2;
-            return new(line1Start.X, m2 * line1Start.X + c2);
-        }
-
-        // line 2 is vertical
-        if (Math.Abs(line2delta.X) < epsilon)
-        {
-            double c1 = line1Start.Y - line1Start.X * m1;
-            return new(line2Start.X, m1 * line2Start.X + c1);
-        }
-
-        // lines are parallel
-        if (Math.Abs(m1 - m2) < epsilon)
-            return null;
-
-        {
-            double c1 = line1Start.Y - line1Start.X * m1;
-            double c2 = line2Start.Y - line2Start.X * m2;
-            double x = (c1 - c2) / (m2 - m1);
-            return new(x, m1 * x + c1);
-        }
-    }
-
-    public static bool IsCorner(Anchor anchor)
-    {
-        return anchor is Anchor.TopLeft or Anchor.TopRight or Anchor.BottomRight or Anchor.BottomLeft;
-    }
-
-    public static bool IsSide(Anchor anchor)
-    {
-        return anchor is Anchor.Left or Anchor.Right or Anchor.Top or Anchor.Bottom;
-    }
-
-    public static Anchor GetOpposite(Anchor anchor)
-    {
-        return anchor switch
-        {
-            Anchor.TopLeft => Anchor.BottomRight,
-            Anchor.TopRight => Anchor.BottomLeft,
-            Anchor.BottomLeft => Anchor.TopRight,
-            Anchor.BottomRight => Anchor.TopLeft,
-            Anchor.Top => Anchor.Bottom,
-            Anchor.Left => Anchor.Right,
-            Anchor.Right => Anchor.Left,
-            Anchor.Bottom => Anchor.Top,
-            _ => throw new ArgumentException($"{anchor} is not a corner or a side"),
-        };
-    }
-
-    /// <summary>
-    /// The first anchor would be on your left if you were standing on the side and looking inside the shape; the second anchor is to the right.
-    /// </summary>
-    public static (Anchor leftAnchor, Anchor rightAnchor) GetCornersOnSide(Anchor side)
-    {
-        return side switch
-        {
-            Anchor.Left => (Anchor.TopLeft, Anchor.BottomLeft),
-            Anchor.Right => (Anchor.BottomRight, Anchor.TopRight),
-            Anchor.Top => (Anchor.TopRight, Anchor.TopLeft),
-            Anchor.Bottom => (Anchor.BottomLeft, Anchor.BottomRight),
-            _ => throw new ArgumentException($"{side} is not a side anchor"),
-        };
-    }
-
-    /// <summary>
-    /// The first corner would be on your left if you were standing on the passed corner and looking inside the shape; the second corner is to the right.
-    /// </summary>
-    public static (Anchor, Anchor) GetNeighboringCorners(Anchor corner)
-    {
-        return corner switch
-        {
-            Anchor.TopLeft => (Anchor.TopRight, Anchor.BottomLeft),
-            Anchor.TopRight => (Anchor.BottomRight, Anchor.TopLeft),
-            Anchor.BottomLeft => (Anchor.TopLeft, Anchor.BottomRight),
-            Anchor.BottomRight => (Anchor.BottomLeft, Anchor.TopRight),
-            _ => throw new ArgumentException($"{corner} is not a corner anchor"),
-        };
-    }
-
-    public static ShapeCorners UpdateCorner(ShapeCorners original, Anchor corner, VecD newPos)
-    {
-        if (corner == Anchor.TopLeft)
-            original.TopLeft = newPos;
-        else if (corner == Anchor.BottomLeft)
-            original.BottomLeft = newPos;
-        else if (corner == Anchor.TopRight)
-            original.TopRight = newPos;
-        else if (corner == Anchor.BottomRight)
-            original.BottomRight = newPos;
-        else
-            throw new ArgumentException($"{corner} is not a corner");
-        return original;
-    }
-
-    public static VecD GetAnchorPosition(ShapeCorners corners, Anchor anchor)
-    {
-        return anchor switch
-        {
-            Anchor.TopLeft => corners.TopLeft,
-            Anchor.BottomRight => corners.BottomRight,
-            Anchor.TopRight => corners.TopRight,
-            Anchor.BottomLeft => corners.BottomLeft,
-            Anchor.Top => corners.TopLeft.Lerp(corners.TopRight, 0.5),
-            Anchor.Bottom => corners.BottomLeft.Lerp(corners.BottomRight, 0.5),
-            Anchor.Left => corners.TopLeft.Lerp(corners.BottomLeft, 0.5),
-            Anchor.Right => corners.BottomRight.Lerp(corners.TopRight, 0.5),
-            _ => throw new ArgumentException($"{anchor} is not a corner or a side"),
-        };
-    }
-
-    public static Anchor? GetAnchorInPosition(VecD pos, ShapeCorners corners, VecD origin, double zoomboxScale)
-    {
-        VecD topLeft = corners.TopLeft;
-        VecD topRight = corners.TopRight;
-        VecD bottomLeft = corners.BottomLeft;
-        VecD bottomRight = corners.BottomRight;
-
-        // corners
-        if (IsWithinAnchor(topLeft, pos, zoomboxScale))
-            return Anchor.TopLeft;
-        if (IsWithinAnchor(topRight, pos, zoomboxScale))
-            return Anchor.TopRight;
-        if (IsWithinAnchor(bottomLeft, pos, zoomboxScale))
-            return Anchor.BottomLeft;
-        if (IsWithinAnchor(bottomRight, pos, zoomboxScale))
-            return Anchor.BottomRight;
-
-        // sides
-        if (IsWithinAnchor((bottomLeft - topLeft) / 2 + topLeft, pos, zoomboxScale))
-            return Anchor.Left;
-        if (IsWithinAnchor((bottomRight - topRight) / 2 + topRight, pos, zoomboxScale))
-            return Anchor.Right;
-        if (IsWithinAnchor((topLeft - topRight) / 2 + topRight, pos, zoomboxScale))
-            return Anchor.Top;
-        if (IsWithinAnchor((bottomLeft - bottomRight) / 2 + bottomRight, pos, zoomboxScale))
-            return Anchor.Bottom;
-
-        // origin
-        if (IsWithinAnchor(origin, pos, zoomboxScale))
-            return Anchor.Origin;
-        return null;
-    }
-
-    public static bool IsWithinAnchor(VecD anchorPos, VecD mousePos, double zoomboxScale)
-    {
-        var delta = (anchorPos - mousePos).Abs();
-        double scaled = AnchorSize / zoomboxScale / 2;
-        return delta.X < scaled && delta.Y < scaled;
-    }
-
-    public static bool IsWithinTransformHandle(VecD handlePos, VecD mousePos, double zoomboxScale)
-    {
-        var delta = (handlePos - mousePos).Abs();
-        double scaled = MoveHandleSize / zoomboxScale / 2;
-        return delta.X < scaled && delta.Y < scaled;
-    }
-
-    public static VecD GetDragHandlePos(ShapeCorners corners, double zoomboxScale)
-    {
-        VecD max = new(
-            Math.Max(Math.Max(corners.TopLeft.X, corners.TopRight.X), Math.Max(corners.BottomLeft.X, corners.BottomRight.X)),
-            Math.Max(Math.Max(corners.TopLeft.Y, corners.TopRight.Y), Math.Max(corners.BottomLeft.Y, corners.BottomRight.Y)));
-        return max + new VecD(MoveHandleSize / zoomboxScale, MoveHandleSize / zoomboxScale);
-    }
-}

+ 0 - 324
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformOverlay.cs

@@ -1,324 +0,0 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Media;
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-
-internal class TransformOverlay : Control
-{
-    public static readonly DependencyProperty RequestedCornersProperty =
-        DependencyProperty.Register(nameof(RequestedCorners), typeof(ShapeCorners), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(default(ShapeCorners), FrameworkPropertyMetadataOptions.AffectsRender, OnRequestedCorners));
-
-    public static readonly DependencyProperty CornersProperty =
-        DependencyProperty.Register(nameof(Corners), typeof(ShapeCorners), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(default(ShapeCorners), FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public static readonly DependencyProperty ZoomboxScaleProperty =
-        DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public static readonly DependencyProperty SideFreedomProperty =
-        DependencyProperty.Register(nameof(SideFreedom), typeof(TransformSideFreedom), typeof(TransformOverlay),
-            new PropertyMetadata(TransformSideFreedom.Locked));
-
-    public static readonly DependencyProperty CornerFreedomProperty =
-        DependencyProperty.Register(nameof(CornerFreedom), typeof(TransformCornerFreedom), typeof(TransformOverlay),
-            new PropertyMetadata(TransformCornerFreedom.Locked));
-
-    public static readonly DependencyProperty SnapToAnglesProperty =
-        DependencyProperty.Register(nameof(SnapToAngles), typeof(bool), typeof(TransformOverlay), new PropertyMetadata(false));
-
-    public static readonly DependencyProperty InternalStateProperty =
-        DependencyProperty.Register(nameof(InternalState), typeof(TransformState), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(default(TransformState), FrameworkPropertyMetadataOptions.AffectsRender));
-
-    public TransformState InternalState
-    {
-        get => (TransformState)GetValue(InternalStateProperty);
-        set => SetValue(InternalStateProperty, value);
-    }
-
-    public bool SnapToAngles
-    {
-        get => (bool)GetValue(SnapToAnglesProperty);
-        set => SetValue(SnapToAnglesProperty, value);
-    }
-
-    public TransformCornerFreedom CornerFreedom
-    {
-        get => (TransformCornerFreedom)GetValue(CornerFreedomProperty);
-        set => SetValue(CornerFreedomProperty, value);
-    }
-
-    public TransformSideFreedom SideFreedom
-    {
-        get => (TransformSideFreedom)GetValue(SideFreedomProperty);
-        set => SetValue(SideFreedomProperty, value);
-    }
-
-    public ShapeCorners Corners
-    {
-        get => (ShapeCorners)GetValue(CornersProperty);
-        set => SetValue(CornersProperty, value);
-    }
-
-    public ShapeCorners RequestedCorners
-    {
-        get => (ShapeCorners)GetValue(RequestedCornersProperty);
-        set => SetValue(RequestedCornersProperty, value);
-    }
-
-    public double ZoomboxScale
-    {
-        get => (double)GetValue(ZoomboxScaleProperty);
-        set => SetValue(ZoomboxScaleProperty, value);
-    }
-
-    private bool isResettingRequestedCorners = false;
-    private bool isMoving = false;
-    private VecD mousePosOnStartMove = new();
-    private VecD originOnStartMove = new();
-    private ShapeCorners cornersOnStartMove = new();
-
-    private bool isRotating = false;
-    private VecD mousePosOnStartRotate = new();
-    private ShapeCorners cornersOnStartRotate = new();
-    private double propAngle1OnStartRotate = 0;
-    private double propAngle2OnStartRotate = 0;
-
-    private Anchor? capturedAnchor;
-    private ShapeCorners cornersOnStartAnchorDrag;
-    private VecD mousePosOnStartAnchorDrag;
-    private VecD originOnStartAnchorDrag;
-
-    private Pen blackPen = new Pen(Brushes.Black, 1);
-    private Pen blackDashedPen = new Pen(Brushes.Black, 1) { DashStyle = new DashStyle(new double[] { 2, 4 }, 0) };
-    private Pen whiteDashedPen = new Pen(Brushes.White, 1) { DashStyle = new DashStyle(new double[] { 2, 4 }, 2) };
-    private Pen blackFreqDashedPen = new Pen(Brushes.Black, 1) { DashStyle = new DashStyle(new double[] { 2, 2 }, 0) };
-    private Pen whiteFreqDashedPen = new Pen(Brushes.White, 1) { DashStyle = new DashStyle(new double[] { 2, 2 }, 2) };
-
-    private PathGeometry handleGeometry = new()
-    {
-        FillRule = FillRule.Nonzero,
-        Figures = (PathFigureCollection?)new PathFigureCollectionConverter()
-            .ConvertFrom("M 0.50025839 0 0.4248062 0.12971572 0.34987079 0.25994821 h 0.1002584 V 0.45012906 H 0.25994831 V 0.34987066 L 0.12971577 0.42480604 0 0.5002582 0.12971577 0.57519373 0.25994831 0.65012926 V 0.5498709 H 0.45012919 V 0.74005175 H 0.34987079 L 0.42480619 0.87028439 0.50025839 1 0.57519399 0.87028439 0.65012959 0.74005175 H 0.54987119 V 0.5498709 H 0.74005211 V 0.65012926 L 0.87028423 0.57519358 1 0.5002582 0.87028423 0.42480604 0.74005169 0.34987066 v 0.1002584 H 0.54987077 V 0.25994821 h 0.1002584 L 0.5751938 0.12971572 Z"),
-    };
-
-    protected override void OnRender(DrawingContext drawingContext)
-    {
-        base.OnRender(drawingContext);
-        DrawOverlay(drawingContext, new(ActualWidth, ActualHeight), Corners, InternalState.Origin, ZoomboxScale);
-    }
-
-    private void DrawOverlay
-        (DrawingContext context, VecD size, ShapeCorners corners, VecD origin, double zoomboxScale)
-    {
-        // draw transparent background to enable mouse input everywhere
-        context.DrawRectangle(Brushes.Transparent, null, new Rect(new Point(0, 0), new Size(size.X, size.Y)));
-
-        blackPen.Thickness = 1 / zoomboxScale;
-        blackDashedPen.Thickness = 1 / zoomboxScale;
-        whiteDashedPen.Thickness = 1 / zoomboxScale;
-        blackFreqDashedPen.Thickness = 1 / zoomboxScale;
-        whiteFreqDashedPen.Thickness = 1 / zoomboxScale;
-
-        VecD topLeft = corners.TopLeft;
-        VecD topRight = corners.TopRight;
-        VecD bottomLeft = corners.BottomLeft;
-        VecD bottomRight = corners.BottomRight;
-
-        // lines
-        context.DrawLine(blackDashedPen, TransformHelper.ToPoint(topLeft), TransformHelper.ToPoint(topRight));
-        context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(topLeft), TransformHelper.ToPoint(topRight));
-        context.DrawLine(blackDashedPen, TransformHelper.ToPoint(topLeft), TransformHelper.ToPoint(bottomLeft));
-        context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(topLeft), TransformHelper.ToPoint(bottomLeft));
-        context.DrawLine(blackDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(bottomLeft));
-        context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(bottomLeft));
-        context.DrawLine(blackDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
-        context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
-
-        // corner anchors
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(topLeft, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(topRight, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(bottomLeft, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(bottomRight, zoomboxScale));
-
-        // side anchors
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect((topLeft - topRight) / 2 + topRight, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect((topLeft - bottomLeft) / 2 + bottomLeft, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect((bottomLeft - bottomRight) / 2 + bottomRight, zoomboxScale));
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect((topRight - bottomRight) / 2 + bottomRight, zoomboxScale));
-
-        // origin
-        double radius = TransformHelper.AnchorSize / zoomboxScale / 2;
-        context.DrawEllipse(Brushes.Transparent, blackFreqDashedPen, TransformHelper.ToPoint(origin), radius, radius);
-        context.DrawEllipse(Brushes.Transparent, whiteFreqDashedPen, TransformHelper.ToPoint(origin), radius, radius);
-
-        // move handle
-        VecD handlePos = TransformHelper.GetDragHandlePos(corners, zoomboxScale);
-        const double CrossSize = TransformHelper.MoveHandleSize - 1;
-        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToHandleRect(handlePos, zoomboxScale));
-        handleGeometry.Transform = new MatrixTransform(
-            0, CrossSize / zoomboxScale,
-            CrossSize / zoomboxScale, 0,
-            handlePos.X - CrossSize / (zoomboxScale * 2), handlePos.Y - CrossSize / (zoomboxScale * 2)
-        );
-        context.DrawGeometry(Brushes.Black, null, handleGeometry);
-    }
-
-    protected override void OnMouseDown(MouseButtonEventArgs e)
-    {
-        base.OnMouseDown(e);
-
-        e.Handled = true;
-        var pos = TransformHelper.ToVecD(e.GetPosition(this));
-        var anchor = TransformHelper.GetAnchorInPosition(pos, Corners, InternalState.Origin, ZoomboxScale);
-        if (anchor is not null)
-        {
-            capturedAnchor = anchor;
-            cornersOnStartAnchorDrag = Corners;
-            originOnStartAnchorDrag = InternalState.Origin;
-            mousePosOnStartAnchorDrag = pos;
-        }
-        else if (Corners.IsPointInside(pos) || TransformHelper.IsWithinTransformHandle(TransformHelper.GetDragHandlePos(Corners, ZoomboxScale), pos, ZoomboxScale))
-        {
-            isMoving = true;
-            mousePosOnStartMove = TransformHelper.ToVecD(e.GetPosition(this));
-            originOnStartMove = InternalState.Origin;
-            cornersOnStartMove = Corners;
-        }
-        else
-        {
-            isRotating = true;
-            mousePosOnStartRotate = TransformHelper.ToVecD(e.GetPosition(this));
-            cornersOnStartRotate = Corners;
-            propAngle1OnStartRotate = InternalState.ProportionalAngle1;
-            propAngle2OnStartRotate = InternalState.ProportionalAngle2;
-        }
-        CaptureMouse();
-    }
-
-    protected override void OnMouseMove(MouseEventArgs e)
-    {
-        if (capturedAnchor is not null)
-        {
-            HandleCapturedAnchorMovement(e);
-            return;
-        }
-
-        if (isMoving)
-        {
-            var pos = TransformHelper.ToVecD(e.GetPosition(this));
-            var delta = pos - mousePosOnStartMove;
-
-            if (Corners.IsSnappedToPixels)
-                delta = delta.Round();
-
-            Corners = new ShapeCorners()
-            {
-                BottomLeft = cornersOnStartMove.BottomLeft + delta, BottomRight = cornersOnStartMove.BottomRight + delta, TopLeft = cornersOnStartMove.TopLeft + delta, TopRight = cornersOnStartMove.TopRight + delta,
-            };
-
-            InternalState = InternalState with { Origin = originOnStartMove + delta };
-        }
-        else if (isRotating)
-        {
-            var pos = TransformHelper.ToVecD(e.GetPosition(this));
-            var angle = (mousePosOnStartRotate - InternalState.Origin).CCWAngleTo(pos - InternalState.Origin);
-            if (SnapToAngles)
-                angle = TransformHelper.FindSnappingAngle(cornersOnStartRotate, angle);
-            InternalState = InternalState with { ProportionalAngle1 = propAngle1OnStartRotate + angle, ProportionalAngle2 = propAngle2OnStartRotate + angle, };
-            Corners = TransformUpdateHelper.UpdateShapeFromRotation(cornersOnStartRotate, InternalState.Origin, angle);
-        }
-    }
-
-    private void HandleCapturedAnchorMovement(MouseEventArgs e)
-    {
-        if (capturedAnchor is null)
-            throw new InvalidOperationException("No anchor is captured");
-        e.Handled = true;
-        if (TransformHelper.IsCorner((Anchor)capturedAnchor) && CornerFreedom == TransformCornerFreedom.Locked ||
-            TransformHelper.IsSide((Anchor)capturedAnchor) && SideFreedom == TransformSideFreedom.Locked)
-            return;
-
-        var pos = TransformHelper.ToVecD(e.GetPosition(this));
-
-        if (TransformHelper.IsCorner((Anchor)capturedAnchor))
-        {
-            var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
-            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner
-                ((Anchor)capturedAnchor, CornerFreedom, InternalState.ProportionalAngle1, InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
-            if (newCorners is not null)
-            {
-                bool shouldSnap = (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) && Corners.IsSnappedToPixels;
-                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
-            }
-            if (!InternalState.OriginWasManuallyDragged)
-                InternalState = InternalState with { Origin = TransformHelper.OriginFromCorners(Corners) };
-        }
-        else if (TransformHelper.IsSide((Anchor)capturedAnchor))
-        {
-            var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
-            var newCorners = TransformUpdateHelper.UpdateShapeFromSide
-                ((Anchor)capturedAnchor, SideFreedom, InternalState.ProportionalAngle1, InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
-            if (newCorners is not null)
-            {
-                bool shouldSnap = (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) && Corners.IsSnappedToPixels;
-                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
-            }
-            if (!InternalState.OriginWasManuallyDragged)
-                InternalState = InternalState with { Origin = TransformHelper.OriginFromCorners(Corners) };
-        }
-        else if (capturedAnchor == Anchor.Origin)
-        {
-            InternalState = InternalState with { OriginWasManuallyDragged = true, Origin = originOnStartAnchorDrag + pos - mousePosOnStartAnchorDrag, };
-        }
-    }
-
-    protected override void OnMouseUp(MouseButtonEventArgs e)
-    {
-        base.OnMouseUp(e);
-        if (ReleaseAnchor())
-            e.Handled = true;
-        else if (isMoving)
-        {
-            isMoving = false;
-            e.Handled = true;
-            ReleaseMouseCapture();
-        }
-        else if (isRotating)
-        {
-            isRotating = false;
-            e.Handled = true;
-            ReleaseMouseCapture();
-        }
-    }
-
-    private static void OnRequestedCorners(DependencyObject obj, DependencyPropertyChangedEventArgs args)
-    {
-        TransformOverlay overlay = (TransformOverlay)obj;
-        if (overlay.isResettingRequestedCorners)
-            return;
-        overlay.Corners = (ShapeCorners)args.NewValue;
-        overlay.InternalState = new()
-        {
-            ProportionalAngle1 = (overlay.Corners.BottomRight - overlay.Corners.TopLeft).Angle, ProportionalAngle2 = (overlay.Corners.TopRight - overlay.Corners.BottomLeft).Angle, OriginWasManuallyDragged = false, Origin = TransformHelper.OriginFromCorners(overlay.Corners),
-        };
-        overlay.isResettingRequestedCorners = true;
-        overlay.RequestedCorners = new ShapeCorners();
-        overlay.isResettingRequestedCorners = false;
-    }
-
-    private bool ReleaseAnchor()
-    {
-        if (capturedAnchor is null)
-            return false;
-        ReleaseMouseCapture();
-        capturedAnchor = null;
-        return true;
-    }
-}

+ 0 - 9
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformSideFreedom.cs

@@ -1,9 +0,0 @@
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-internal enum TransformSideFreedom
-{
-    Locked,
-    ScaleProportionally,
-    Stretch,
-    Shear,
-    Free
-}

+ 0 - 10
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformState.cs

@@ -1,10 +0,0 @@
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-internal struct TransformState
-{
-    public bool OriginWasManuallyDragged { get; set; }
-    public VecD Origin { get; set; }
-    public double ProportionalAngle1 { get; set; }
-    public double ProportionalAngle2 { get; set; }
-}

+ 0 - 160
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformUpdateHelper.cs

@@ -1,160 +0,0 @@
-using System;
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.CustomControls.TransformOverlay;
-internal static class TransformUpdateHelper
-{
-    public static ShapeCorners? UpdateShapeFromCorner
-        (Anchor targetCorner, TransformCornerFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners, VecD desiredPos)
-    {
-        if (!TransformHelper.IsCorner(targetCorner))
-            throw new ArgumentException($"{targetCorner} is not a corner");
-
-        if (freedom == TransformCornerFreedom.Locked)
-            return corners;
-
-        if (freedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale)
-        {
-            VecD targetPos = TransformHelper.GetAnchorPosition(corners, targetCorner);
-            Anchor opposite = TransformHelper.GetOpposite(targetCorner);
-            VecD oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
-
-            if (freedom == TransformCornerFreedom.ScaleProportionally)
-            {
-                double correctAngle = targetCorner is Anchor.TopLeft or Anchor.BottomRight ? propAngle1 : propAngle2;
-                desiredPos = desiredPos.ProjectOntoLine(oppositePos, oppositePos + VecD.FromAngleAndLength(correctAngle, 1));
-            }
-
-            (Anchor leftNeighbor, Anchor rightNeighbor) = TransformHelper.GetNeighboringCorners(targetCorner);
-            VecD leftNeighborPos = TransformHelper.GetAnchorPosition(corners, leftNeighbor);
-            VecD rightNeighborPos = TransformHelper.GetAnchorPosition(corners, rightNeighbor);
-
-            double angle = corners.RectRotation;
-            VecD targetTrans = (targetPos - oppositePos).Rotate(-angle);
-            VecD leftNeighTrans = (leftNeighborPos - oppositePos).Rotate(-angle);
-            VecD rightNeighTrans = (rightNeighborPos - oppositePos).Rotate(-angle);
-
-            VecD delta = (desiredPos - targetPos).Rotate(-angle);
-
-            corners = TransformHelper.UpdateCorner(corners, targetCorner,
-                (targetTrans + delta).Rotate(angle) + oppositePos);
-            corners = TransformHelper.UpdateCorner(corners, leftNeighbor,
-                (leftNeighTrans + delta.Multiply(leftNeighTrans.Divide(targetTrans))).Rotate(angle) + oppositePos);
-            corners = TransformHelper.UpdateCorner(corners, rightNeighbor,
-                (rightNeighTrans + delta.Multiply(rightNeighTrans.Divide(targetTrans))).Rotate(angle) + oppositePos);
-
-            return corners;
-        }
-
-        if (freedom == TransformCornerFreedom.Free)
-        {
-            ShapeCorners newCorners = TransformHelper.UpdateCorner(corners, targetCorner, desiredPos);
-            return newCorners.IsLegal ? newCorners : null;
-        }
-        throw new ArgumentException($"Freedom degree {freedom} is not supported");
-    }
-
-    public static ShapeCorners? UpdateShapeFromSide
-        (Anchor targetSide, TransformSideFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners, VecD desiredPos)
-    {
-        if (!TransformHelper.IsSide(targetSide))
-            throw new ArgumentException($"{targetSide} is not a side");
-
-        if (freedom == TransformSideFreedom.Locked)
-            return corners;
-
-        if (freedom is TransformSideFreedom.ScaleProportionally)
-        {
-            var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
-            var opposite = TransformHelper.GetOpposite(targetSide);
-            var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
-
-            desiredPos = desiredPos.ProjectOntoLine(targetPos, oppositePos);
-
-            VecD thing = targetPos - oppositePos;
-            thing = VecD.FromAngleAndLength(thing.Angle, 1 / thing.Length);
-            double scalingFactor = (desiredPos - oppositePos) * thing;
-            if (!double.IsNormal(scalingFactor))
-                return corners;
-
-            if (corners.IsRect)
-            {
-                var delta = desiredPos - targetPos;
-                var center = oppositePos.Lerp(desiredPos, 0.5);
-
-                var (leftCorn, rightCorn) = TransformHelper.GetCornersOnSide(targetSide);
-                var (leftOppCorn, _) = TransformHelper.GetNeighboringCorners(leftCorn);
-                var (_, rightOppCorn) = TransformHelper.GetNeighboringCorners(rightCorn);
-
-                var leftCornPos = TransformHelper.GetAnchorPosition(corners, leftCorn);
-                var rightCornPos = TransformHelper.GetAnchorPosition(corners, rightCorn);
-                var leftOppCornPos = TransformHelper.GetAnchorPosition(corners, leftOppCorn);
-                var rightOppCornPos = TransformHelper.GetAnchorPosition(corners, rightOppCorn);
-
-                var (leftAngle, rightAngle) = leftCorn is Anchor.TopLeft or Anchor.BottomRight ? (propAngle1, propAngle2) : (propAngle2, propAngle1);
-
-                var updLeftCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta, center, center + VecD.FromAngleAndLength(leftAngle, 1));
-                var updRightCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta, center, center + VecD.FromAngleAndLength(rightAngle, 1));
-                var updLeftOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center, center + VecD.FromAngleAndLength(rightAngle, 1));
-                var updRightOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center, center + VecD.FromAngleAndLength(leftAngle, 1));
-
-                if (updLeftCorn is null || updRightCorn is null || updLeftOppCorn is null || updRightOppCorn is null)
-                    goto fallback;
-
-                corners = TransformHelper.UpdateCorner(corners, leftCorn, (VecD)updLeftCorn);
-                corners = TransformHelper.UpdateCorner(corners, rightCorn, (VecD)updRightCorn);
-                corners = TransformHelper.UpdateCorner(corners, leftOppCorn, (VecD)updLeftOppCorn);
-                corners = TransformHelper.UpdateCorner(corners, rightOppCorn, (VecD)updRightOppCorn);
-
-                return corners;
-            }
-fallback:
-            corners.TopLeft = (corners.TopLeft - oppositePos) * scalingFactor + oppositePos;
-            corners.BottomRight = (corners.BottomRight - oppositePos) * scalingFactor + oppositePos;
-            corners.TopRight = (corners.TopRight - oppositePos) * scalingFactor + oppositePos;
-            corners.BottomLeft = (corners.BottomLeft - oppositePos) * scalingFactor + oppositePos;
-
-            if (scalingFactor < 0)
-            {
-                corners.TopLeft = corners.TopLeft.ReflectAcrossLine(targetPos, oppositePos);
-                corners.BottomRight = corners.BottomRight.ReflectAcrossLine(targetPos, oppositePos);
-                corners.TopRight = corners.TopRight.ReflectAcrossLine(targetPos, oppositePos);
-                corners.BottomLeft = corners.BottomLeft.ReflectAcrossLine(targetPos, oppositePos);
-            }
-
-            return corners;
-        }
-
-        if (freedom is TransformSideFreedom.Shear or TransformSideFreedom.Stretch or TransformSideFreedom.Free)
-        {
-            var (leftCorner, rightCorner) = TransformHelper.GetCornersOnSide(targetSide);
-            var leftCornerPos = TransformHelper.GetAnchorPosition(corners, leftCorner);
-            var rightCornerPos = TransformHelper.GetAnchorPosition(corners, rightCorner);
-            var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
-
-            var opposite = TransformHelper.GetOpposite(targetSide);
-            var oppPos = TransformHelper.GetAnchorPosition(corners, opposite);
-
-            if (freedom == TransformSideFreedom.Shear)
-                desiredPos = desiredPos.ProjectOntoLine(leftCornerPos, rightCornerPos);
-            else if (freedom == TransformSideFreedom.Stretch)
-                desiredPos = desiredPos.ProjectOntoLine(targetPos, oppPos);
-
-            var delta = desiredPos - targetPos;
-            var newCorners = TransformHelper.UpdateCorner(corners, leftCorner, leftCornerPos + delta);
-            newCorners = TransformHelper.UpdateCorner(newCorners, rightCorner, rightCornerPos + delta);
-
-            return newCorners.IsLegal ? newCorners : null;
-        }
-        throw new ArgumentException($"Freedom degree {freedom} is not supported");
-    }
-
-    public static ShapeCorners UpdateShapeFromRotation(ShapeCorners corners, VecD origin, double angle)
-    {
-        corners.TopLeft = corners.TopLeft.Rotate(angle, origin);
-        corners.TopRight = corners.TopRight.Rotate(angle, origin);
-        corners.BottomLeft = corners.BottomLeft.Rotate(angle, origin);
-        corners.BottomRight = corners.BottomRight.Rotate(angle, origin);
-        return corners;
-    }
-}

+ 0 - 1
src/PixiEditorPrototype/GlobalUsings.cs

@@ -1 +0,0 @@
-global using PixiEditor.ChangeableDocument.Actions.Generated;

+ 0 - 42
src/PixiEditorPrototype/Helpers/SurfaceHelpers.cs

@@ -1,42 +0,0 @@
-using System;
-using System.Windows;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using ChunkyImageLib;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.Helpers;
-
-public static class SurfaceHelpers
-{
-    public static WriteableBitmap ToWriteableBitmap(this Surface surface)
-    {
-        int width = surface.Size.X;
-        int height = surface.Size.Y;
-        WriteableBitmap result = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
-        result.Lock();
-        var dirty = new Int32Rect(0, 0, width, height);
-        result.WritePixels(dirty, ToByteArray(surface), width * 4, 0);
-        result.AddDirtyRect(dirty);
-        result.Unlock();
-        return result;
-    }
-
-    private static unsafe byte[] ToByteArray(Surface surface, SKColorType colorType = SKColorType.Bgra8888, SKAlphaType alphaType = SKAlphaType.Premul)
-    {
-        int width = surface.Size.X;
-        int height = surface.Size.Y;
-        var imageInfo = new SKImageInfo(width, height, colorType, alphaType, SKColorSpace.CreateSrgb());
-
-        byte[] buffer = new byte[width * height * imageInfo.BytesPerPixel];
-        fixed (void* pointer = buffer)
-        {
-            if (!surface.SkiaSurface.ReadPixels(imageInfo, new IntPtr(pointer), imageInfo.RowBytes, 0, 0))
-            {
-                throw new InvalidOperationException("Could not read surface into buffer");
-            }
-        }
-
-        return buffer;
-    }
-}

+ 0 - 190
src/PixiEditorPrototype/Models/ActionAccumulator.cs

@@ -1,190 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows;
-using System.Windows.Threading;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Actions;
-using PixiEditor.ChangeableDocument.Actions.Undo;
-using PixiEditor.ChangeableDocument.ChangeInfos;
-using PixiEditorPrototype.Models.Rendering;
-using PixiEditorPrototype.Models.Rendering.RenderInfos;
-using PixiEditorPrototype.ViewModels;
-
-namespace PixiEditorPrototype.Models;
-
-internal class ActionAccumulator
-{
-    private bool executing = false;
-
-    private List<IAction> queuedActions = new();
-    private DocumentViewModel document;
-    private DocumentHelpers helpers;
-
-    private WriteableBitmapUpdater renderer;
-
-    public ActionAccumulator(DocumentViewModel doc, DocumentHelpers helpers)
-    {
-        this.document = doc;
-        this.helpers = helpers;
-
-        renderer = new(doc, helpers);
-    }
-
-    public void AddFinishedActions(params IAction[] actions)
-    {
-        queuedActions.AddRange(actions);
-        queuedActions.Add(new ChangeBoundary_Action());
-        TryExecuteAccumulatedActions();
-    }
-
-    public void AddActions(params IAction[] actions)
-    {
-        queuedActions.AddRange(actions);
-        TryExecuteAccumulatedActions();
-    }
-
-    private async void TryExecuteAccumulatedActions()
-    {
-        if (executing || queuedActions.Count == 0)
-            return;
-        executing = true;
-        DispatcherTimer busyTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(2000) };
-        busyTimer.Tick += (_, _) =>
-        {
-            busyTimer.Stop();
-            document.Busy = true;
-        };
-        busyTimer.Start();
-
-        while (queuedActions.Count > 0)
-        {
-            // select actions to be processed
-            var toExecute = queuedActions;
-            queuedActions = new List<IAction>();
-
-            // pass them to changeabledocument for processing
-            List<IChangeInfo?> changes;
-            if (AreAllPassthrough(toExecute))
-                changes = toExecute.Select(a => (IChangeInfo?)a).ToList();
-            else
-                changes = await helpers.Tracker.ProcessActions(toExecute);
-
-            // update viewmodels based on changes
-            foreach (IChangeInfo? info in changes)
-            {
-                helpers.Updater.ApplyChangeFromChangeInfo(info);
-            }
-
-            // lock bitmaps that need to be updated
-            var affectedChunks = new AffectedChunkGatherer(helpers.Tracker, changes);
-
-            foreach (var (_, bitmap) in document.Bitmaps)
-            {
-                bitmap.Lock();
-            }
-            bool refreshDelayed = toExecute.Any(static action => action is ChangeBoundary_Action or Redo_Action or Undo_Action);
-            if (refreshDelayed)
-                LockPreviewBitmaps(document.StructureRoot);
-
-            // update bitmaps
-            var renderResult = await renderer.UpdateGatheredChunks(affectedChunks, refreshDelayed);
-            AddDirtyRects(renderResult);
-
-            // unlock bitmaps
-            foreach (var (_, bitmap) in document.Bitmaps)
-            {
-                bitmap.Unlock();
-            }
-            if (refreshDelayed)
-                UnlockPreviewBitmaps(document.StructureRoot);
-
-            // force refresh viewports for better responsiveness
-            foreach (var (_, value) in helpers.State.Viewports)
-            {
-                value.InvalidateVisual();
-            }
-        }
-
-        busyTimer.Stop();
-        if (document.Busy)
-            document.Busy = false;
-        executing = false;
-    }
-
-    private bool AreAllPassthrough(List<IAction> actions)
-    {
-        foreach (var action in actions)
-        {
-            if (action is not IChangeInfo)
-                return false;
-        }
-        return true;
-    }
-
-    private void LockPreviewBitmaps(FolderViewModel root)
-    {
-        foreach (var child in root.Children)
-        {
-            child.PreviewBitmap.Lock();
-            if (child.MaskPreviewBitmap is not null)
-                child.MaskPreviewBitmap.Lock();
-            if (child is FolderViewModel innerFolder)
-                LockPreviewBitmaps(innerFolder);
-        }
-        document.PreviewBitmap.Lock();
-    }
-
-    private void UnlockPreviewBitmaps(FolderViewModel root)
-    {
-        foreach (var child in root.Children)
-        {
-            child.PreviewBitmap.Unlock();
-            if (child.MaskPreviewBitmap is not null)
-                child.MaskPreviewBitmap.Unlock();
-            if (child is FolderViewModel innerFolder)
-                UnlockPreviewBitmaps(innerFolder);
-        }
-        document.PreviewBitmap.Unlock();
-    }
-
-    private void AddDirtyRects(List<IRenderInfo> changes)
-    {
-        foreach (IRenderInfo renderInfo in changes)
-        {
-            switch (renderInfo)
-            {
-                case DirtyRect_RenderInfo info:
-                {
-                    var bitmap = document.Bitmaps[info.Resolution];
-                    RectI finalRect = new RectI(VecI.Zero, new(bitmap.PixelWidth, bitmap.PixelHeight));
-
-                    RectI dirtyRect = new RectI(info.Pos, info.Size).Intersect(finalRect);
-                    bitmap.AddDirtyRect(new(dirtyRect.Left, dirtyRect.Top, dirtyRect.Width, dirtyRect.Height));
-                }
-                    break;
-                case PreviewDirty_RenderInfo info:
-                {
-                    var bitmap = helpers.StructureHelper.Find(info.GuidValue)?.PreviewBitmap;
-                    if (bitmap is null)
-                        continue;
-                    bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
-                }
-                    break;
-                case MaskPreviewDirty_RenderInfo info:
-                {
-                    var bitmap = helpers.StructureHelper.Find(info.GuidValue)?.MaskPreviewBitmap;
-                    if (bitmap is null)
-                        continue;
-                    bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
-                }
-                    break;
-                case CanvasPreviewDirty_RenderInfo:
-                {
-                    document.PreviewBitmap.AddDirtyRect(new Int32Rect(0, 0, document.PreviewBitmap.PixelWidth, document.PreviewBitmap.PixelHeight));
-                }
-                    break;
-            }
-        }
-    }
-}

+ 0 - 30
src/PixiEditorPrototype/Models/BlendModeEx.cs

@@ -1,30 +0,0 @@
-using PixiEditor.ChangeableDocument.Enums;
-
-namespace PixiEditorPrototype.Models;
-internal static class BlendModeEx
-{
-    public static string EnglishName(this BlendMode mode)
-    {
-        return mode switch
-        {
-            BlendMode.Normal => "Normal",
-            BlendMode.Darken => "Darken",
-            BlendMode.Multiply => "Multiply",
-            BlendMode.ColorBurn => "Color Burn",
-            BlendMode.Lighten => "Lighten",
-            BlendMode.Screen => "Screen",
-            BlendMode.ColorDodge => "Color Dodge",
-            BlendMode.LinearDodge => "Linear Dodge (Add)",
-            BlendMode.Overlay => "Overlay",
-            BlendMode.SoftLight => "Soft Light",
-            BlendMode.HardLight => "Hard Light",
-            BlendMode.Difference => "Difference",
-            BlendMode.Exclusion => "Exclusion",
-            BlendMode.Hue => "Hue",
-            BlendMode.Saturation => "Saturation",
-            BlendMode.Luminosity => "Luminosity",
-            BlendMode.Color => "Color",
-            _ => "<no name>",
-        };
-    }
-}

+ 0 - 21
src/PixiEditorPrototype/Models/DocumentHelpers.cs

@@ -1,21 +0,0 @@
-using PixiEditor.ChangeableDocument;
-using PixiEditorPrototype.ViewModels;
-
-namespace PixiEditorPrototype.Models;
-
-internal class DocumentHelpers
-{
-    public DocumentHelpers(DocumentViewModel doc)
-    {
-        Tracker = new DocumentChangeTracker();
-        StructureHelper = new DocumentStructureHelper(doc, this);
-        Updater = new DocumentUpdater(doc, this);
-        ActionAccumulator = new ActionAccumulator(doc, this);
-        State = new DocumentState();
-    }
-    public ActionAccumulator ActionAccumulator { get; }
-    public DocumentChangeTracker Tracker { get; }
-    public DocumentStructureHelper StructureHelper { get; }
-    public DocumentUpdater Updater { get; }
-    public DocumentState State { get; }
-}

+ 0 - 9
src/PixiEditorPrototype/Models/DocumentState.cs

@@ -1,9 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace PixiEditorPrototype.Models;
-
-internal class DocumentState
-{
-    public Dictionary<Guid, ViewportInfo> Viewports { get; set; } = new();
-}

+ 0 - 144
src/PixiEditorPrototype/Models/DocumentStructureHelper.cs

@@ -1,144 +0,0 @@
-using System;
-using System.Collections.Generic;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditorPrototype.ViewModels;
-
-namespace PixiEditorPrototype.Models;
-
-internal class DocumentStructureHelper
-{
-    private DocumentViewModel doc;
-    private DocumentHelpers helpers;
-    public DocumentStructureHelper(DocumentViewModel doc, DocumentHelpers helpers)
-    {
-        this.doc = doc;
-        this.helpers = helpers;
-    }
-
-    public void CreateNewStructureMember(StructureMemberType type)
-    {
-        StructureMemberViewModel? member = doc.FindFirstSelectedMember();
-        if (member is null)
-        {
-            Guid guid = Guid.NewGuid();
-            //put member on top
-            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, guid, doc.StructureRoot.Children.Count, type));
-            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
-            return;
-        }
-        if (member is FolderViewModel folder)
-        {
-            Guid guid = Guid.NewGuid();
-            //put member inside folder on top
-            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(folder.GuidValue, guid, folder.Children.Count, type));
-            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
-            return;
-        }
-        if (member is LayerViewModel layer)
-        {
-            Guid guid = Guid.NewGuid();
-            //put member above the layer
-            List<StructureMemberViewModel>? path = FindPath(layer.GuidValue);
-            if (path.Count < 2)
-                throw new InvalidOperationException("Couldn't find a path to the selected member");
-            FolderViewModel? parent = (FolderViewModel)path[1];
-            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(parent.GuidValue, guid, parent.Children.IndexOf(layer) + 1, type));
-            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
-            return;
-        }
-        throw new ArgumentException("Unknown member type: " + type.ToString());
-    }
-
-    public StructureMemberViewModel FindOrThrow(Guid guid) => Find(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
-    public StructureMemberViewModel? Find(Guid guid)
-    {
-        List<StructureMemberViewModel>? list = FindPath(guid);
-        return list.Count > 0 ? list[0] : null;
-    }
-
-    public StructureMemberViewModel? FindFirstWhere(Predicate<StructureMemberViewModel> predicate)
-    {
-        return FindFirstWhere(predicate, doc.StructureRoot);
-    }
-    private StructureMemberViewModel? FindFirstWhere(Predicate<StructureMemberViewModel> predicate, FolderViewModel folderVM)
-    {
-        foreach (StructureMemberViewModel? child in folderVM.Children)
-        {
-            if (predicate(child))
-                return child;
-            if (child is FolderViewModel innerFolderVM)
-            {
-                StructureMemberViewModel? result = FindFirstWhere(predicate, innerFolderVM);
-                if (result is not null)
-                    return result;
-            }
-        }
-        return null;
-    }
-
-    public (StructureMemberViewModel, FolderViewModel) FindChildAndParentOrThrow(Guid childGuid)
-    {
-        List<StructureMemberViewModel>? path = FindPath(childGuid);
-        if (path.Count < 2)
-            throw new ArgumentException("Couldn't find child and parent");
-        return (path[0], (FolderViewModel)path[1]);
-    }
-    public List<StructureMemberViewModel> FindPath(Guid guid)
-    {
-        List<StructureMemberViewModel>? list = new List<StructureMemberViewModel>();
-        if (FillPath(doc.StructureRoot, guid, list))
-            list.Add(doc.StructureRoot);
-        return list;
-    }
-
-    private bool FillPath(FolderViewModel folder, Guid guid, List<StructureMemberViewModel> toFill)
-    {
-        if (folder.GuidValue == guid)
-        {
-            return true;
-        }
-        foreach (StructureMemberViewModel? member in folder.Children)
-        {
-            if (member is LayerViewModel childLayer && childLayer.GuidValue == guid)
-            {
-                toFill.Add(member);
-                return true;
-            }
-            if (member is FolderViewModel childFolder)
-            {
-                if (FillPath(childFolder, guid, toFill))
-                {
-                    toFill.Add(childFolder);
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public void MoveStructureMember(Guid guid, bool toSmallerIndex)
-    {
-        List<StructureMemberViewModel>? path = FindPath(guid);
-        if (path.Count < 2)
-            throw new ArgumentException("Couldn't find the member to be moved");
-        if (path.Count == 2)
-        {
-            int curIndex = doc.StructureRoot.Children.IndexOf(path[0]);
-            if ((curIndex == 0 && toSmallerIndex) || (curIndex == doc.StructureRoot.Children.Count - 1 && !toSmallerIndex))
-                return;
-            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, doc.StructureRoot.GuidValue, toSmallerIndex ? curIndex - 1 : curIndex + 1));
-            return;
-        }
-        FolderViewModel? folder = (FolderViewModel)path[1];
-        int index = folder.Children.IndexOf(path[0]);
-        if ((toSmallerIndex && index > 0) || (!toSmallerIndex && index < folder.Children.Count - 1))
-        {
-            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[1].GuidValue, toSmallerIndex ? index - 1 : index + 1));
-        }
-        else
-        {
-            int parentIndex = ((FolderViewModel)path[2]).Children.IndexOf(folder);
-            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[2].GuidValue, toSmallerIndex ? parentIndex : parentIndex + 1));
-        }
-    }
-}

+ 0 - 319
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -1,319 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.ChangeInfos;
-using PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
-using PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-using PixiEditor.ChangeableDocument.ChangeInfos.Root;
-using PixiEditor.ChangeableDocument.ChangeInfos.Structure;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditorPrototype.ViewModels;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.Models;
-
-internal class DocumentUpdater
-{
-    private DocumentViewModel doc;
-    private DocumentHelpers helper;
-
-    public DocumentUpdater(DocumentViewModel doc, DocumentHelpers helper)
-    {
-        this.doc = doc;
-        this.helper = helper;
-    }
-
-    /// <summary>
-    /// Don't call this outside ActionAccumulator
-    /// </summary>
-    public void ApplyChangeFromChangeInfo(IChangeInfo? arbitraryInfo)
-    {
-        if (arbitraryInfo is null)
-            return;
-
-        switch (arbitraryInfo)
-        {
-            case CreateStructureMember_ChangeInfo info:
-                ProcessCreateStructureMember(info);
-                break;
-            case DeleteStructureMember_ChangeInfo info:
-                ProcessDeleteStructureMember(info);
-                break;
-            case StructureMemberName_ChangeInfo info:
-                ProcessUpdateStructureMemberName(info);
-                break;
-            case StructureMemberIsVisible_ChangeInfo info:
-                ProcessUpdateStructureMemberIsVisible(info);
-                break;
-            case StructureMemberOpacity_ChangeInfo info:
-                ProcessUpdateStructureMemberOpacity(info);
-                break;
-            case MoveStructureMember_ChangeInfo info:
-                ProcessMoveStructureMember(info);
-                break;
-            case Size_ChangeInfo info:
-                ProcessSize(info);
-                break;
-            case RefreshViewport_PassthroughAction info:
-                ProcessRefreshViewport(info);
-                break;
-            case RemoveViewport_PassthroughAction info:
-                ProcessRemoveViewport(info);
-                break;
-            case StructureMemberMask_ChangeInfo info:
-                ProcessStructureMemberMask(info);
-                break;
-            case StructureMemberBlendMode_ChangeInfo info:
-                ProcessStructureMemberBlendMode(info);
-                break;
-            case LayerLockTransparency_ChangeInfo info:
-                ProcessLayerLockTransparency(info);
-                break;
-            case Selection_ChangeInfo info:
-                ProcessSelection(info);
-                break;
-            case SymmetryAxisState_ChangeInfo info:
-                ProcessSymmetryState(info);
-                break;
-            case SymmetryAxisPosition_ChangeInfo info:
-                ProcessSymmetryPosition(info);
-                break;
-            case StructureMemberClipToMemberBelow_ChangeInfo info:
-                ProcessClipToMemberBelow(info);
-                break;
-            case StructureMemberMaskIsVisible_ChangeInfo info:
-                ProcessMaskIsVisible(info);
-                break;
-            case CreateReferenceLayer_ChangeInfo info:
-                ProcessCreateReferenceLayer(info);
-                break;
-        }
-    }
-
-    private void ProcessCreateReferenceLayer(CreateReferenceLayer_ChangeInfo info)
-    {
-        doc.RaisePropertyChanged(nameof(doc.ReferenceLayer));
-        doc.RaisePropertyChanged(nameof(doc.ReferenceBitmap));
-        doc.RaisePropertyChanged(nameof(doc.ReferenceBitmapSize));
-        doc.RaisePropertyChanged(nameof(doc.ReferenceTransformMatrix));
-        doc.RaisePropertyChanged(nameof(doc.ReferenceShape));
-    }
-
-    private void ProcessMaskIsVisible(StructureMemberMaskIsVisible_ChangeInfo info)
-    {
-        StructureMemberViewModel? member = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        member.SetMaskIsVisible(info.IsVisible);
-    }
-
-    private void ProcessClipToMemberBelow(StructureMemberClipToMemberBelow_ChangeInfo info)
-    {
-        var member = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        member.SetClipToMemberBelowEnabled(info.ClipToMemberBelow);
-    }
-
-    private void ProcessSymmetryPosition(SymmetryAxisPosition_ChangeInfo info)
-    {
-        if (info.Direction == SymmetryAxisDirection.Horizontal)
-            doc.SetHorizontalSymmetryAxisY(info.NewPosition);
-        else if (info.Direction == SymmetryAxisDirection.Vertical)
-            doc.SetVerticalSymmetryAxisX(info.NewPosition);
-    }
-
-    private void ProcessSymmetryState(SymmetryAxisState_ChangeInfo info)
-    {
-        if (info.Direction == SymmetryAxisDirection.Horizontal)
-            doc.SetHorizontalSymmetryAxisEnabled(info.State);
-        else if (info.Direction == SymmetryAxisDirection.Vertical)
-            doc.SetVerticalSymmetryAxisEnabled(info.State);
-    }
-
-    private void ProcessSelection(Selection_ChangeInfo info)
-    {
-        doc.SetSelectionPath(info.NewPath);
-    }
-
-    private void ProcessLayerLockTransparency(LayerLockTransparency_ChangeInfo info)
-    {
-        var layer = (LayerViewModel)helper.StructureHelper.FindOrThrow(info.GuidValue);
-        layer.SetLockTransparency(info.LockTransparency);
-    }
-
-    private void ProcessStructureMemberBlendMode(StructureMemberBlendMode_ChangeInfo info)
-    {
-        var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVm.SetBlendMode(info.BlendMode);
-    }
-
-    private void ProcessStructureMemberMask(StructureMemberMask_ChangeInfo info)
-    {
-        StructureMemberViewModel? memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVm.MaskPreviewSurface?.Dispose();
-        memberVm.MaskPreviewSurface = null;
-        memberVm.MaskPreviewBitmap = null;
-
-        if (info.HasMask)
-        {
-            VecI size = StructureMemberViewModel.CalculatePreviewSize(doc.SizeBindable);
-            memberVm.MaskPreviewBitmap = CreateBitmap(size);
-            memberVm.MaskPreviewSurface = CreateSKSurface(memberVm.MaskPreviewBitmap);
-        }
-        memberVm.SetHasMask(info.HasMask);
-        memberVm.RaisePropertyChanged(nameof(memberVm.MaskPreviewBitmap));
-    }
-
-    private void ProcessRefreshViewport(RefreshViewport_PassthroughAction info)
-    {
-        helper.State.Viewports[info.Info.GuidValue] = info.Info;
-    }
-
-    private void ProcessRemoveViewport(RemoveViewport_PassthroughAction info)
-    {
-        helper.State.Viewports.Remove(info.GuidValue);
-    }
-
-    private void UpdateMemberBitmapsRecursively(FolderViewModel folder, VecI newSize)
-    {
-        foreach (StructureMemberViewModel? member in folder.Children)
-        {
-            member.PreviewSurface.Dispose();
-            member.PreviewBitmap = CreateBitmap(newSize);
-            member.PreviewSurface = CreateSKSurface(member.PreviewBitmap);
-            member.RaisePropertyChanged(nameof(member.PreviewBitmap));
-
-            member.MaskPreviewSurface?.Dispose();
-            member.MaskPreviewSurface = null;
-            member.MaskPreviewBitmap = null;
-            if (member.HasMaskBindable)
-            {
-                member.MaskPreviewBitmap = CreateBitmap(newSize);
-                member.MaskPreviewSurface = CreateSKSurface(member.MaskPreviewBitmap);
-            }
-            member.RaisePropertyChanged(nameof(member.MaskPreviewBitmap));
-
-            if (member is FolderViewModel innerFolder)
-            {
-                UpdateMemberBitmapsRecursively(innerFolder, newSize);
-            }
-        }
-    }
-
-    private void ProcessSize(Size_ChangeInfo info)
-    {
-        Dictionary<ChunkResolution, WriteableBitmap> newBitmaps = new();
-        foreach ((ChunkResolution res, SKSurface surf) in doc.Surfaces)
-        {
-            surf.Dispose();
-            newBitmaps[res] = CreateBitmap((VecI)(info.Size * res.Multiplier()));
-            doc.Surfaces[res] = CreateSKSurface(newBitmaps[res]);
-        }
-
-        doc.Bitmaps = newBitmaps;
-
-        doc.SetSize(info.Size);
-        doc.SetVerticalSymmetryAxisX(info.VerticalSymmetryAxisX);
-        doc.SetHorizontalSymmetryAxisY(info.HorizontalSymmetryAxisY);
-
-        VecI previewSize = StructureMemberViewModel.CalculatePreviewSize(info.Size);
-        doc.PreviewSurface.Dispose();
-        doc.PreviewBitmap = CreateBitmap(previewSize);
-        doc.PreviewSurface = CreateSKSurface(doc.PreviewBitmap);
-
-        doc.RaisePropertyChanged(nameof(doc.Bitmaps));
-        doc.RaisePropertyChanged(nameof(doc.PreviewBitmap));
-
-        UpdateMemberBitmapsRecursively(doc.StructureRoot, previewSize);
-    }
-
-    private WriteableBitmap CreateBitmap(VecI size)
-    {
-        return new WriteableBitmap(Math.Max(size.X, 1), Math.Max(size.Y, 1), 96, 96, PixelFormats.Pbgra32, null);
-    }
-
-    private SKSurface CreateSKSurface(WriteableBitmap bitmap)
-    {
-        return SKSurface.Create(
-            new SKImageInfo(bitmap.PixelWidth, bitmap.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
-            bitmap.BackBuffer,
-            bitmap.BackBufferStride);
-    }
-
-    private void ProcessCreateStructureMember(CreateStructureMember_ChangeInfo info)
-    {
-        FolderViewModel? parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(info.ParentGuid);
-
-        StructureMemberViewModel memberVM;
-        if (info is CreateLayer_ChangeInfo layerInfo)
-        {
-            memberVM = new LayerViewModel(doc, helper, info.GuidValue);
-            ((LayerViewModel)memberVM).SetLockTransparency(layerInfo.LockTransparency);
-        }
-        else if (info is CreateFolder_ChangeInfo)
-        {
-            memberVM = new FolderViewModel(doc, helper, info.GuidValue);
-        }
-        else
-        {
-            throw new NotSupportedException();
-        }
-        memberVM.SetOpacity(info.Opacity);
-        memberVM.SetIsVisible(info.IsVisible);
-        memberVM.SetClipToMemberBelowEnabled(info.ClipToMemberBelow);
-        memberVM.SetName(info.Name);
-        memberVM.SetHasMask(info.HasMask);
-        memberVM.SetMaskIsVisible(info.MaskIsVisible);
-        memberVM.SetBlendMode(info.BlendMode);
-
-        if (info.HasMask)
-        {
-            VecI size = StructureMemberViewModel.CalculatePreviewSize(doc.SizeBindable);
-            memberVM.MaskPreviewBitmap = CreateBitmap(size);
-            memberVM.MaskPreviewSurface = CreateSKSurface(memberVM.MaskPreviewBitmap);
-        }
-
-        parentFolderVM.Children.Insert(info.Index, memberVM);
-
-        if (info is CreateFolder_ChangeInfo folderInfo)
-        {
-            foreach (CreateStructureMember_ChangeInfo childInfo in folderInfo.Children)
-            {
-                ProcessCreateStructureMember(childInfo);
-            }
-        }
-    }
-
-    private void ProcessDeleteStructureMember(DeleteStructureMember_ChangeInfo info)
-    {
-        (StructureMemberViewModel memberVM, FolderViewModel folderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-        folderVM.Children.Remove(memberVM);
-    }
-
-    private void ProcessUpdateStructureMemberIsVisible(StructureMemberIsVisible_ChangeInfo info)
-    {
-        StructureMemberViewModel? memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.SetIsVisible(info.IsVisible);
-    }
-
-    private void ProcessUpdateStructureMemberName(StructureMemberName_ChangeInfo info)
-    {
-        StructureMemberViewModel? memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.SetName(info.Name);
-    }
-
-    private void ProcessUpdateStructureMemberOpacity(StructureMemberOpacity_ChangeInfo info)
-    {
-        StructureMemberViewModel? memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.SetOpacity(info.Opacity);
-    }
-
-    private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
-    {
-        (StructureMemberViewModel memberVM, FolderViewModel curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-
-        FolderViewModel? targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(info.ParentToGuid);
-
-        curFolderVM.Children.Remove(memberVM);
-        targetFolderVM.Children.Insert(info.NewIndex, memberVM);
-    }
-}

+ 0 - 18
src/PixiEditorPrototype/Models/IReadOnlyListEx.cs

@@ -1,18 +0,0 @@
-using System.Collections.Generic;
-
-namespace PixiEditorPrototype.Models;
-
-internal static class IReadOnlyListEx
-{
-    public static int IndexOf<T>(this IReadOnlyList<T> self, T elementToFind)
-    {
-        int i = 0;
-        foreach (T element in self)
-        {
-            if (Equals(element, elementToFind))
-                return i;
-            i++;
-        }
-        return -1;
-    }
-}

+ 0 - 6
src/PixiEditorPrototype/Models/RefreshViewport_PassthroughAction.cs

@@ -1,6 +0,0 @@
-using PixiEditor.ChangeableDocument.Actions;
-using PixiEditor.ChangeableDocument.ChangeInfos;
-
-namespace PixiEditorPrototype.Models;
-
-internal record class RefreshViewport_PassthroughAction(ViewportInfo Info) : IAction, IChangeInfo;

+ 0 - 6
src/PixiEditorPrototype/Models/RemoveViewport_PassthroughAction.cs

@@ -1,6 +0,0 @@
-using System;
-using PixiEditor.ChangeableDocument.Actions;
-using PixiEditor.ChangeableDocument.ChangeInfos;
-
-namespace PixiEditorPrototype.Models;
-internal record class RemoveViewport_PassthroughAction(Guid GuidValue) : IAction, IChangeInfo;

+ 0 - 226
src/PixiEditorPrototype/Models/Rendering/AffectedChunkGatherer.cs

@@ -1,226 +0,0 @@
-using System;
-using System.Collections.Generic;
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using PixiEditor.ChangeableDocument.ChangeInfos;
-using PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
-using PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-using PixiEditor.ChangeableDocument.ChangeInfos.Root;
-using PixiEditor.ChangeableDocument.ChangeInfos.Structure;
-
-namespace PixiEditorPrototype.Models.Rendering;
-
-internal class AffectedChunkGatherer
-{
-    private readonly DocumentChangeTracker tracker;
-
-    public HashSet<VecI> mainImageChunks { get; private set; } = new();
-    public Dictionary<Guid, HashSet<VecI>> imagePreviewChunks { get; private set; } = new();
-    public Dictionary<Guid, HashSet<VecI>> maskPreviewChunks { get; private set; } = new();
-
-    public AffectedChunkGatherer(DocumentChangeTracker tracker, IReadOnlyList<IChangeInfo?> changes)
-    {
-        this.tracker = tracker;
-        ProcessChanges(changes);
-    }
-
-    private void ProcessChanges(IReadOnlyList<IChangeInfo?> changes)
-    {
-        foreach (var change in changes)
-        {
-            switch (change)
-            {
-                case MaskChunks_ChangeInfo info:
-                    if (info.Chunks is null)
-                        throw new InvalidOperationException("Chunks must not be null");
-                    AddToMainImage(info.Chunks);
-                    AddToImagePreviews(info.GuidValue, info.Chunks, true);
-                    AddToMaskPreview(info.GuidValue, info.Chunks);
-                    break;
-                case LayerImageChunks_ChangeInfo info:
-                    if (info.Chunks is null)
-                        throw new InvalidOperationException("Chunks must not be null");
-                    AddToMainImage(info.Chunks);
-                    AddToImagePreviews(info.GuidValue, info.Chunks);
-                    break;
-                case CreateStructureMember_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue);
-                    AddAllToMaskPreview(info.GuidValue);
-                    break;
-                case DeleteStructureMember_ChangeInfo info:
-                    AddWholeCanvasToMainImage();
-                    AddWholeCanvasToImagePreviews(info.ParentGuid);
-                    break;
-                case MoveStructureMember_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    if (info.ParentFromGuid != info.ParentToGuid)
-                        AddWholeCanvasToImagePreviews(info.ParentFromGuid);
-                    break;
-                case Size_ChangeInfo:
-                    AddWholeCanvasToMainImage();
-                    AddWholeCanvasToEveryImagePreview();
-                    AddWholeCanvasToEveryMaskPreview();
-                    break;
-                case StructureMemberMask_ChangeInfo info:
-                    AddWholeCanvasToMainImage();
-                    AddWholeCanvasToMaskPreview(info.GuidValue);
-                    AddWholeCanvasToImagePreviews(info.GuidValue, true);
-                    break;
-                case StructureMemberBlendMode_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    break;
-                case StructureMemberClipToMemberBelow_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    break;
-                case StructureMemberOpacity_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    break;
-                case StructureMemberIsVisible_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    break;
-                case StructureMemberMaskIsVisible_ChangeInfo info:
-                    AddAllToMainImage(info.GuidValue, false);
-                    AddAllToImagePreviews(info.GuidValue, true);
-                    break;
-            }
-        }
-    }
-
-    private void AddAllToImagePreviews(Guid memberGuid, bool ignoreSelf = false)
-    {
-        var member = tracker.Document.FindMember(memberGuid);
-        if (member is IReadOnlyLayer layer)
-        {
-            var chunks = layer.LayerImage.FindAllChunks();
-            AddToImagePreviews(memberGuid, chunks, ignoreSelf);
-        }
-        else if (member is IReadOnlyFolder folder)
-        {
-            AddWholeCanvasToImagePreviews(memberGuid, ignoreSelf);
-            foreach (var child in folder.Children)
-                AddAllToImagePreviews(child.GuidValue);
-        }
-    }
-
-    private void AddAllToMainImage(Guid memberGuid, bool useMask = true)
-    {
-        var member = tracker.Document.FindMember(memberGuid);
-        if (member is IReadOnlyLayer layer)
-        {
-            var chunks = layer.LayerImage.FindAllChunks();
-            if (layer.Mask is not null && layer.MaskIsVisible && useMask)
-                chunks.IntersectWith(layer.Mask.FindAllChunks());
-            AddToMainImage(chunks);
-        }
-        else
-        {
-            AddWholeCanvasToMainImage();
-        }
-    }
-
-    private void AddAllToMaskPreview(Guid memberGuid)
-    {
-        if (!tracker.Document.TryFindMember(memberGuid, out var member))
-            return;
-        if (member.Mask is not null)
-        {
-            var chunks = member.Mask.FindAllChunks();
-            AddToMaskPreview(memberGuid, chunks);
-        }
-        if (member is IReadOnlyFolder folder)
-        {
-            foreach (var child in folder.Children)
-                AddAllToMaskPreview(child.GuidValue);
-        }
-    }
-
-
-    private void AddToMainImage(HashSet<VecI> chunks)
-    {
-        mainImageChunks.UnionWith(chunks);
-    }
-
-    private void AddToImagePreviews(Guid memberGuid, HashSet<VecI> chunks, bool ignoreSelf = false)
-    {
-        var path = tracker.Document.FindMemberPath(memberGuid);
-        if (path.Count < 2)
-            return;
-        for (int i = ignoreSelf ? 1 : 0; i < path.Count - 1; i++)
-        {
-            var member = path[i];
-            if (!imagePreviewChunks.ContainsKey(member.GuidValue))
-                imagePreviewChunks[member.GuidValue] = new HashSet<VecI>(chunks);
-            else
-                imagePreviewChunks[member.GuidValue].UnionWith(chunks);
-        }
-    }
-
-    private void AddToMaskPreview(Guid memberGuid, HashSet<VecI> chunks)
-    {
-        if (!maskPreviewChunks.ContainsKey(memberGuid))
-            maskPreviewChunks[memberGuid] = new HashSet<VecI>(chunks);
-        else
-            maskPreviewChunks[memberGuid].UnionWith(chunks);
-    }
-
-
-    private void AddWholeCanvasToMainImage()
-    {
-        AddAllChunks(mainImageChunks);
-    }
-
-    private void AddWholeCanvasToImagePreviews(Guid memberGuid, bool ignoreSelf = false)
-    {
-        var path = tracker.Document.FindMemberPath(memberGuid);
-        if (path.Count < 2)
-            return;
-        // skip root folder
-        for (int i = ignoreSelf ? 1 : 0; i < path.Count - 1; i++)
-        {
-            var member = path[i];
-            if (!imagePreviewChunks.ContainsKey(member.GuidValue))
-                imagePreviewChunks[member.GuidValue] = new HashSet<VecI>();
-            AddAllChunks(imagePreviewChunks[member.GuidValue]);
-        }
-    }
-
-    private void AddWholeCanvasToMaskPreview(Guid memberGuid)
-    {
-        if (!maskPreviewChunks.ContainsKey(memberGuid))
-            maskPreviewChunks[memberGuid] = new HashSet<VecI>();
-        AddAllChunks(maskPreviewChunks[memberGuid]);
-    }
-
-
-    private void AddWholeCanvasToEveryImagePreview()
-    {
-        tracker.Document.ForEveryReadonlyMember((member) => AddWholeCanvasToImagePreviews(member.GuidValue));
-    }
-
-    private void AddWholeCanvasToEveryMaskPreview()
-    {
-        tracker.Document.ForEveryReadonlyMember((member) => AddWholeCanvasToMaskPreview(member.GuidValue));
-    }
-
-    private void AddAllChunks(HashSet<VecI> chunks)
-    {
-        VecI size = new(
-            (int)Math.Ceiling(tracker.Document.Size.X / (float)ChunkyImage.FullChunkSize),
-            (int)Math.Ceiling(tracker.Document.Size.Y / (float)ChunkyImage.FullChunkSize));
-        for (int i = 0; i < size.X; i++)
-        {
-            for (int j = 0; j < size.Y; j++)
-            {
-                chunks.Add(new(i, j));
-            }
-        }
-    }
-}

+ 0 - 3
src/PixiEditorPrototype/Models/Rendering/RenderInfos/CanvasPreviewDirty_RenderInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditorPrototype.Models.Rendering.RenderInfos;
-
-internal record CanvasPreviewDirty_RenderInfo : IRenderInfo;

+ 0 - 5
src/PixiEditorPrototype/Models/Rendering/RenderInfos/DirtyRect_RenderInfo.cs

@@ -1,5 +0,0 @@
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.Models.Rendering.RenderInfos;
-
-public record class DirtyRect_RenderInfo(VecI Pos, VecI Size, ChunkResolution Resolution) : IRenderInfo;

+ 0 - 5
src/PixiEditorPrototype/Models/Rendering/RenderInfos/IRenderInfo.cs

@@ -1,5 +0,0 @@
-namespace PixiEditorPrototype.Models.Rendering.RenderInfos;
-
-public interface IRenderInfo
-{
-}

+ 0 - 5
src/PixiEditorPrototype/Models/Rendering/RenderInfos/MaskPreviewDirty_RenderInfo.cs

@@ -1,5 +0,0 @@
-using System;
-
-namespace PixiEditorPrototype.Models.Rendering.RenderInfos;
-
-public record class MaskPreviewDirty_RenderInfo(Guid GuidValue) : IRenderInfo;

+ 0 - 5
src/PixiEditorPrototype/Models/Rendering/RenderInfos/PreviewDirty_RenderInfo.cs

@@ -1,5 +0,0 @@
-using System;
-
-namespace PixiEditorPrototype.Models.Rendering.RenderInfos;
-
-public record class PreviewDirty_RenderInfo(Guid GuidValue) : IRenderInfo;

+ 0 - 303
src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs

@@ -1,303 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
-using ChunkyImageLib.Operations;
-using OneOf;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditorPrototype.Models.Rendering.RenderInfos;
-using PixiEditorPrototype.ViewModels;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.Models.Rendering;
-
-internal class WriteableBitmapUpdater
-{
-    private readonly DocumentViewModel doc;
-    private readonly DocumentHelpers helpers;
-
-    private static readonly SKPaint ReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
-    private static readonly SKPaint SmoothReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src, FilterQuality = SKFilterQuality.Medium, IsAntialias = true };
-    private static readonly SKPaint ClearPaint = new SKPaint() { BlendMode = SKBlendMode.Src, Color = SKColors.Transparent };
-
-    /// <summary>
-    /// Chunks that have been updated but don't need to be re-rendered because they are out of view
-    /// </summary>
-    private readonly Dictionary<ChunkResolution, HashSet<VecI>> globalPostponedChunks = new() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
-
-    /// <summary>
-    /// The state of globalPostponedChunks during the last update of global delayed chunks (when you finish using a tool)
-    /// It is required in case the viewport is moved while you are using a tool. In this case the newly visible chunks on delayed viewports
-    /// need to be re-rendered, even though normally re-render only happens after you're done with some tool.
-    /// Because the viewport still has the old version of the image there is no point in re-rendering everything from globalPostponedChunks.
-    /// It's enough to re-render the chunks that were postponed back when the delayed viewports were last updated fully.
-    /// </summary>
-    private readonly Dictionary<ChunkResolution, HashSet<VecI>> globalPostponedForDelayed = new() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
-
-    /// <summary>
-    /// Chunks that have been updated but don't need to be re-rendered because all viewports that see them have Delayed == true
-    /// These chunks are updated after you finish using a tool
-    /// </summary>
-    private readonly Dictionary<ChunkResolution, HashSet<VecI>> globalDelayedChunks = new() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
-
-    private Dictionary<Guid, HashSet<VecI>> previewDelayedChunks = new();
-    private Dictionary<Guid, HashSet<VecI>> maskPreviewDelayedChunks = new();
-
-    public WriteableBitmapUpdater(DocumentViewModel doc, DocumentHelpers helpers)
-    {
-        this.doc = doc;
-        this.helpers = helpers;
-    }
-
-    /// <summary>
-    /// Don't call this outside ActionAccumulator
-    /// </summary>
-    public async Task<List<IRenderInfo>> UpdateGatheredChunks
-        (AffectedChunkGatherer chunkGatherer, bool updateDelayed)
-    {
-        return await Task.Run(() => Render(chunkGatherer, updateDelayed)).ConfigureAwait(true);
-    }
-
-    private Dictionary<ChunkResolution, HashSet<VecI>> FindGlobalChunksToRerender(AffectedChunkGatherer chunkGatherer, bool renderDelayed)
-    {
-        // add all affected chunks to postponed
-        foreach (var (_, postponed) in globalPostponedChunks)
-        {
-            postponed.UnionWith(chunkGatherer.mainImageChunks);
-        }
-
-        // find all chunks that are on viewports and on delayed viewports
-        var chunksToUpdate = new Dictionary<ChunkResolution, HashSet<VecI>>() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
-
-        var chunksOnDelayedViewports = new Dictionary<ChunkResolution, HashSet<VecI>>() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
-
-        foreach (var (_, viewport) in helpers.State.Viewports)
-        {
-            var viewportChunks = OperationHelper.FindChunksTouchingRectangle(
-                viewport.Center,
-                viewport.Dimensions,
-                -viewport.Angle,
-                ChunkResolution.Full.PixelSize());
-            if (viewport.Delayed)
-                chunksOnDelayedViewports[viewport.Resolution].UnionWith(viewportChunks);
-            else
-                chunksToUpdate[viewport.Resolution].UnionWith(viewportChunks);
-        }
-
-        // exclude the chunks that don't need to be updated, remove chunks that will be updated from postponed
-        foreach (var (res, postponed) in globalPostponedChunks)
-        {
-            chunksToUpdate[res].IntersectWith(postponed);
-            chunksOnDelayedViewports[res].IntersectWith(postponed);
-            postponed.ExceptWith(chunksToUpdate[res]);
-        }
-
-        // decide what to do about the delayed chunks
-        if (renderDelayed)
-        {
-            foreach (var (res, postponed) in globalPostponedChunks)
-            {
-                chunksToUpdate[res].UnionWith(chunksOnDelayedViewports[res]);
-                postponed.ExceptWith(chunksOnDelayedViewports[res]);
-                globalPostponedForDelayed[res] = new HashSet<VecI>(postponed);
-            }
-        }
-        else
-        {
-            foreach (var (res, postponed) in globalPostponedChunks)
-            {
-                chunksOnDelayedViewports[res].IntersectWith(globalPostponedForDelayed[res]);
-                globalPostponedForDelayed[res].ExceptWith(chunksOnDelayedViewports[res]);
-
-                chunksToUpdate[res].UnionWith(chunksOnDelayedViewports[res]);
-                postponed.ExceptWith(chunksOnDelayedViewports[res]);
-            }
-        }
-
-        return chunksToUpdate;
-    }
-
-
-    private static void AddChunks(Dictionary<Guid, HashSet<VecI>> from, Dictionary<Guid, HashSet<VecI>> to)
-    {
-        foreach ((Guid guid, HashSet<VecI> chunks) in from)
-        {
-            if (!to.ContainsKey(guid))
-                to[guid] = new HashSet<VecI>();
-            to[guid].UnionWith(chunks);
-        }
-    }
-
-    private (Dictionary<Guid, HashSet<VecI>> image, Dictionary<Guid, HashSet<VecI>> mask) FindPreviewChunksToRerender
-        (AffectedChunkGatherer chunkGatherer, bool postpone)
-    {
-        AddChunks(chunkGatherer.imagePreviewChunks, previewDelayedChunks);
-        AddChunks(chunkGatherer.maskPreviewChunks, maskPreviewDelayedChunks);
-        if (postpone)
-            return (new(), new());
-        var result = (previewPostponedChunks: previewDelayedChunks, maskPostponedChunks: maskPreviewDelayedChunks);
-        previewDelayedChunks = new();
-        maskPreviewDelayedChunks = new();
-        return result;
-    }
-
-    private List<IRenderInfo> Render(AffectedChunkGatherer chunkGatherer, bool updateDelayed)
-    {
-        Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender = FindGlobalChunksToRerender(chunkGatherer, updateDelayed);
-
-        List<IRenderInfo> infos = new();
-        UpdateMainImage(chunksToRerender, infos);
-
-        var (imagePreviewChunksToRerender, maskPreviewChunksToRerender) = FindPreviewChunksToRerender(chunkGatherer, !updateDelayed);
-        var previewSize = StructureMemberViewModel.CalculatePreviewSize(helpers.Tracker.Document.Size);
-        float scaling = (float)previewSize.X / doc.SizeBindable.X;
-        UpdateImagePreviews(imagePreviewChunksToRerender, scaling, infos);
-        UpdateMaskPreviews(maskPreviewChunksToRerender, scaling, infos);
-
-        return infos;
-    }
-
-    private void UpdateImagePreviews(Dictionary<Guid, HashSet<VecI>> imagePreviewChunks, float scaling, List<IRenderInfo> infos)
-    {
-        // update preview of the whole canvas
-        var cumulative = imagePreviewChunks.Aggregate(new HashSet<VecI>(), (set, pair) =>
-        {
-            set.UnionWith(pair.Value);
-            return set;
-        });
-        bool somethingChanged = false;
-        foreach (var chunkPos in cumulative)
-        {
-            somethingChanged = true;
-            ChunkResolution resolution = scaling switch
-            {
-                > 1 / 2f => ChunkResolution.Full,
-                > 1 / 4f => ChunkResolution.Half,
-                > 1 / 8f => ChunkResolution.Quarter,
-                _ => ChunkResolution.Eighth,
-            };
-            var pos = chunkPos * resolution.PixelSize();
-            var rendered = ChunkRenderer.MergeWholeStructure(chunkPos, resolution, helpers.Tracker.Document.StructureRoot);
-            doc.PreviewSurface.Canvas.Save();
-            doc.PreviewSurface.Canvas.Scale(scaling);
-            doc.PreviewSurface.Canvas.Scale(1 / (float)resolution.Multiplier());
-            if (rendered.IsT1)
-            {
-                doc.PreviewSurface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(), resolution.PixelSize(), ClearPaint);
-                return;
-            }
-            using var renderedChunk = rendered.AsT0;
-            renderedChunk.DrawOnSurface(doc.PreviewSurface, pos, SmoothReplacingPaint);
-            doc.PreviewSurface.Canvas.Restore();
-        }
-        if (somethingChanged)
-            infos.Add(new CanvasPreviewDirty_RenderInfo());
-
-        // update previews of individual members
-        foreach (var (guid, chunks) in imagePreviewChunks)
-        {
-            var memberVM = helpers.StructureHelper.Find(guid);
-            if (memberVM is null)
-                continue;
-            var member = helpers.Tracker.Document.FindMemberOrThrow(guid);
-
-            memberVM.PreviewSurface.Canvas.Save();
-            memberVM.PreviewSurface.Canvas.Scale(scaling);
-            if (memberVM is LayerViewModel)
-            {
-                var layer = (IReadOnlyLayer)member;
-                foreach (var chunk in chunks)
-                {
-                    var pos = chunk * ChunkResolution.Full.PixelSize();
-                    // the full res chunks are already rendered so drawing them again should be fast
-                    if (!layer.LayerImage.DrawMostUpToDateChunkOn
-                            (chunk, ChunkResolution.Full, memberVM.PreviewSurface, pos, SmoothReplacingPaint))
-                        memberVM.PreviewSurface.Canvas.DrawRect(pos.X, pos.Y, ChunkyImage.FullChunkSize, ChunkyImage.FullChunkSize, ClearPaint);
-                }
-                infos.Add(new PreviewDirty_RenderInfo(guid));
-            }
-            else if (memberVM is FolderViewModel)
-            {
-                var folder = (IReadOnlyFolder)member;
-                foreach (var chunk in chunks)
-                {
-                    var pos = chunk * ChunkResolution.Full.PixelSize();
-                    // drawing in full res here is kinda slow
-                    // we could switch to a lower resolution based on (canvas size / preview size) to make it run faster
-                    OneOf<Chunk, EmptyChunk> rendered = ChunkRenderer.MergeWholeStructure(chunk, ChunkResolution.Full, folder);
-                    if (rendered.IsT0)
-                    {
-                        memberVM.PreviewSurface.Canvas.DrawSurface(rendered.AsT0.Surface.SkiaSurface, pos, SmoothReplacingPaint);
-                        rendered.AsT0.Dispose();
-                    }
-                    else
-                    {
-                        memberVM.PreviewSurface.Canvas.DrawRect(pos.X, pos.Y, ChunkResolution.Full.PixelSize(), ChunkResolution.Full.PixelSize(), ClearPaint);
-                    }
-                }
-                infos.Add(new PreviewDirty_RenderInfo(guid));
-            }
-            memberVM.PreviewSurface.Canvas.Restore();
-        }
-    }
-
-    private void UpdateMaskPreviews(Dictionary<Guid, HashSet<VecI>> maskPreviewChunks, float scaling, List<IRenderInfo> infos)
-    {
-        foreach (var (guid, chunks) in maskPreviewChunks)
-        {
-            var memberVM = helpers.StructureHelper.Find(guid);
-            if (memberVM is null || !memberVM.HasMaskBindable)
-                continue;
-
-            var member = helpers.Tracker.Document.FindMemberOrThrow(guid);
-            memberVM.MaskPreviewSurface!.Canvas.Save();
-            memberVM.MaskPreviewSurface.Canvas.Scale(scaling);
-
-            foreach (var chunk in chunks)
-            {
-                var pos = chunk * ChunkResolution.Full.PixelSize();
-                member.Mask!.DrawMostUpToDateChunkOn
-                    (chunk, ChunkResolution.Full, memberVM.MaskPreviewSurface, pos, SmoothReplacingPaint);
-            }
-
-            memberVM.MaskPreviewSurface.Canvas.Restore();
-            infos.Add(new MaskPreviewDirty_RenderInfo(guid));
-        }
-    }
-
-    private void UpdateMainImage(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender, List<IRenderInfo> infos)
-    {
-        foreach (var (resolution, chunks) in chunksToRerender)
-        {
-            int chunkSize = resolution.PixelSize();
-            SKSurface screenSurface = doc.Surfaces[resolution];
-            foreach (var chunkPos in chunks)
-            {
-                RenderChunk(chunkPos, screenSurface, resolution);
-                infos.Add(new DirtyRect_RenderInfo(
-                    chunkPos * chunkSize,
-                    new(chunkSize, chunkSize),
-                    resolution
-                ));
-            }
-        }
-    }
-
-    private void RenderChunk(VecI chunkPos, SKSurface screenSurface, ChunkResolution resolution)
-    {
-        ChunkRenderer.MergeWholeStructure(chunkPos, resolution, helpers.Tracker.Document.StructureRoot).Switch(
-            (Chunk chunk) =>
-            {
-                screenSurface.Canvas.DrawSurface(chunk.Surface.SkiaSurface, chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
-                chunk.Dispose();
-            },
-            (EmptyChunk _) =>
-            {
-                var pos = chunkPos * resolution.PixelSize();
-                screenSurface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(), resolution.PixelSize(), ClearPaint);
-            });
-    }
-}

+ 0 - 22
src/PixiEditorPrototype/Models/Tool.cs

@@ -1,22 +0,0 @@
-namespace PixiEditorPrototype.Models;
-
-internal enum Tool
-{
-    // drawing
-    Rectangle,
-    Line,
-    Ellipse,
-    PathBasedPen,
-    LineBasedPen,
-    PixelPerfectPen,
-    Eraser,
-    ShiftLayer,
-    FloodFill,
-    Brightness,
-    // selection
-    SelectRectangle,
-    SelectEllipse,
-    Lasso,
-    //misc
-    Pipette,
-}

+ 0 - 13
src/PixiEditorPrototype/Models/ViewportInfo.cs

@@ -1,13 +0,0 @@
-using System;
-using ChunkyImageLib.DataHolders;
-
-namespace PixiEditorPrototype.Models;
-internal readonly record struct ViewportInfo(
-    double Angle,
-    VecD Center,
-    VecD RealDimensions,
-    VecD Dimensions,
-    ChunkResolution Resolution,
-    Guid GuidValue,
-    bool Delayed,
-    Action InvalidateVisual);

+ 0 - 32
src/PixiEditorPrototype/PixiEditorPrototype.csproj

@@ -1,32 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>WinExe</OutputType>
-    <TargetFramework>net6.0-windows</TargetFramework>
-    <Nullable>enable</Nullable>
-    <UseWPF>true</UseWPF>
-    <WarningsAsErrors>Nullable</WarningsAsErrors>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Dirkster.AvalonDock" Version="4.70.1" />
-    <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
-    <PackageReference Include="PixiEditor.ColorPicker" Version="3.2.0" />
-    <PackageReference Include="PixiEditor.Parser" Version="2.1.0.3" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\ChunkyImageLib\ChunkyImageLib.csproj" />
-    <ProjectReference Include="..\PixiEditor.ChangeableDocument\PixiEditor.ChangeableDocument.csproj" />
-    <ProjectReference Include="..\PixiEditor.Zoombox\PixiEditor.Zoombox.csproj" />
-  </ItemGroup>
-
-</Project>

+ 0 - 38
src/PixiEditorPrototype/RelayCommand.cs

@@ -1,38 +0,0 @@
-using System;
-using System.Windows.Input;
-
-namespace PixiEditorPrototype;
-
-internal class RelayCommand : ICommand
-{
-    public event EventHandler? CanExecuteChanged;
-
-    private readonly Action<object?> execute;
-    private readonly Func<object?, bool>? canExecute;
-
-    public RelayCommand(Action<object?> execute, Func<object?, bool> canExecute)
-    {
-        this.execute = execute;
-        this.canExecute = canExecute;
-    }
-
-    public RelayCommand(Action<object?> execute)
-    {
-        this.execute = execute;
-    }
-
-    public void RaiseCanExecuteChanged()
-    {
-        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
-    }
-
-    public bool CanExecute(object? parameter)
-    {
-        return canExecute?.Invoke(parameter) ?? true;
-    }
-
-    public void Execute(object? parameter)
-    {
-        execute.Invoke(parameter);
-    }
-}

+ 0 - 42
src/PixiEditorPrototype/ReverseOrderStackPanel.cs

@@ -1,42 +0,0 @@
-using System;
-using System.Linq;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace PixiEditorPrototype;
-
-public class ReversedOrderStackPanel : StackPanel
-{
-    protected override Size ArrangeOverride(Size arrangeSize)
-    {
-        bool fHorizontal = Orientation == Orientation.Horizontal;
-        Rect rcChild = new Rect(arrangeSize);
-        double previousChildSize = 0.0;
-
-        System.Collections.Generic.IEnumerable<UIElement?> children = InternalChildren.Cast<UIElement?>().Reverse();
-        foreach (UIElement? child in children)
-        {
-            if (child is null)
-                continue;
-
-            if (fHorizontal)
-            {
-                rcChild.X += previousChildSize;
-                previousChildSize = child.DesiredSize.Width;
-                rcChild.Width = previousChildSize;
-                rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height);
-            }
-            else
-            {
-                rcChild.Y += previousChildSize;
-                previousChildSize = child.DesiredSize.Height;
-                rcChild.Height = previousChildSize;
-                rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width);
-            }
-
-            child.Arrange(rcChild);
-        }
-
-        return arrangeSize;
-    }
-}

+ 0 - 136
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml

@@ -1,136 +0,0 @@
-<UserControl
-    x:Class="PixiEditorPrototype.UserControls.Viewport.Viewport"
-    x:ClassModifier="internal"
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:local="clr-namespace:PixiEditorPrototype.UserControls.Viewport"
-    xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
-    xmlns:to="clr-namespace:PixiEditorPrototype.CustomControls.TransformOverlay"
-    xmlns:cust="clr-namespace:PixiEditorPrototype.CustomControls"
-    xmlns:sym="clr-namespace:PixiEditorPrototype.CustomControls.SymmetryOverlay"
-    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
-    xmlns:conv="clr-namespace:PixiEditorPrototype.Converters"
-    mc:Ignorable="d"
-    x:Name="vpUc"
-    d:DesignHeight="450"
-    d:DesignWidth="800">
-    <UserControl.Resources>
-        <conv:BoolToVisibilityConverter
-            x:Key="BoolToVisibilityConverter" />
-        <conv:ScaleToBitmapScalingModeConverter
-            x:Key="ScaleToBitmapScalingModeConverter" />
-    </UserControl.Resources>
-    <Grid>
-        <zoombox:Zoombox
-            x:Name="zoombox"
-            UseTouchGestures="True"
-            Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            Angle="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            RealDimensions="{Binding RealDimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            Dimensions="{Binding Dimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=TwoWay}"
-            FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}"
-            FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
-            <Border
-                BorderThickness="1"
-                Background="White"
-                BorderBrush="Black"
-                HorizontalAlignment="Center"
-                VerticalAlignment="Center"
-                DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
-                <Grid>
-                    <Canvas>
-                        <Image
-                            Width="{Binding Document.ReferenceBitmap.Width}"
-                            Height="{Binding Document.ReferenceBitmap.Height}"
-                            Source="{Binding Document.ReferenceBitmap, Mode=OneWay}"
-                            SizeChanged="OnReferenceImageSizeChanged"
-                            RenderOptions.BitmapScalingMode="{Binding ReferenceLayerScale, Converter={StaticResource ScaleToBitmapScalingModeConverter}}">
-                            <Image.RenderTransform>
-                                <TransformGroup>
-                                    <MatrixTransform
-                                        Matrix="{Binding Document.ReferenceTransformMatrix}" />
-                                </TransformGroup>
-                            </Image.RenderTransform>
-                        </Image>
-                    </Canvas>
-                    <Image
-                        Focusable="True"
-                        Width="{Binding Document.Width}"
-                        Height="{Binding Document.Height}"
-                        Source="{Binding TargetBitmap}"
-                        RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={StaticResource ScaleToBitmapScalingModeConverter}}">
-                        <i:Interaction.Triggers>
-                            <i:EventTrigger
-                                EventName="MouseDown">
-                                <i:InvokeCommandAction
-                                    Command="{Binding MouseDownCommand}"
-                                    PassEventArgsToCommand="True" />
-                            </i:EventTrigger>
-                            <i:EventTrigger
-                                EventName="MouseMove">
-                                <i:InvokeCommandAction
-                                    Command="{Binding MouseMoveCommand}"
-                                    PassEventArgsToCommand="True" />
-                            </i:EventTrigger>
-                            <i:EventTrigger
-                                EventName="MouseUp">
-                                <i:InvokeCommandAction
-                                    Command="{Binding MouseUpCommand}"
-                                    PassEventArgsToCommand="True" />
-                            </i:EventTrigger>
-                        </i:Interaction.Triggers>
-                    </Image>
-                    <sym:SymmetryOverlay
-                        ZoomboxScale="{Binding Zoombox.Scale}"
-                        HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
-                        VerticalAxisVisible="{Binding Document.VerticalSymmetryAxisEnabledBindable}"
-                        HorizontalAxisY="{Binding Document.HorizontalSymmetryAxisYBindable, Mode=OneWay}"
-                        VerticalAxisX="{Binding Document.VerticalSymmetryAxisXBindable, Mode=OneWay}"
-                        DragCommand="{Binding Document.DragSymmetryCommand}"
-                        DragEndCommand="{Binding Document.EndDragSymmetryCommand}" />
-                    <cust:SelectionOverlay
-                        Path="{Binding Document.SelectionPathBindable}"
-                        ZoomboxScale="{Binding Zoombox.Scale}" />
-                    <to:TransformOverlay
-                        HorizontalAlignment="Stretch"
-                        VerticalAlignment="Stretch"
-                        Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={StaticResource BoolToVisibilityConverter}}"
-                        Corners="{Binding Document.TransformViewModel.Corners, Mode=TwoWay}"
-                        RequestedCorners="{Binding Document.TransformViewModel.RequestedCorners, Mode=TwoWay}"
-                        CornerFreedom="{Binding Document.TransformViewModel.CornerFreedom}"
-                        SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
-                        InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
-                        ZoomboxScale="{Binding Zoombox.Scale}" />
-                </Grid>
-            </Border>
-        </zoombox:Zoombox>
-        <Grid
-            Focusable="False">
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition
-                    Width="1*" />
-                <ColumnDefinition
-                    Width="2*" />
-                <ColumnDefinition
-                    Width="1*" />
-            </Grid.ColumnDefinitions>
-            <Grid.RowDefinitions>
-                <RowDefinition
-                    Height="1*" />
-                <RowDefinition
-                    Height="2*" />
-                <RowDefinition
-                    Height="1*" />
-            </Grid.RowDefinitions>
-            <Border
-                BorderBrush="Red"
-                Grid.Row="1"
-                Grid.Column="1"
-                BorderThickness="1" />
-        </Grid>
-    </Grid>
-</UserControl>

+ 0 - 257
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml.cs

@@ -1,257 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Input;
-using System.Windows.Media.Imaging;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.Zoombox;
-using PixiEditorPrototype.Models;
-using PixiEditorPrototype.ViewModels;
-
-namespace PixiEditorPrototype.UserControls.Viewport;
-
-internal partial class Viewport : UserControl, INotifyPropertyChanged
-{
-    public event PropertyChangedEventHandler? PropertyChanged;
-
-    public static readonly DependencyProperty FlipXProperty =
-        DependencyProperty.Register(nameof(FlipX), typeof(bool), typeof(Viewport), new(false));
-
-    public static readonly DependencyProperty FlipYProperty =
-        DependencyProperty.Register(nameof(FlipY), typeof(bool), typeof(Viewport), new(false));
-
-    public static readonly DependencyProperty ZoomModeProperty =
-        DependencyProperty.Register(nameof(ZoomMode), typeof(ZoomboxMode), typeof(Viewport), new(ZoomboxMode.Normal));
-
-    public static readonly DependencyProperty DocumentProperty =
-        DependencyProperty.Register(nameof(Document), typeof(DocumentViewModel), typeof(Viewport), new(null, OnDocumentChange));
-
-    public static readonly DependencyProperty MouseDownCommandProperty =
-        DependencyProperty.Register(nameof(MouseDownCommand), typeof(ICommand), typeof(Viewport), new(null));
-
-    public static readonly DependencyProperty MouseMoveCommandProperty =
-        DependencyProperty.Register(nameof(MouseMoveCommand), typeof(ICommand), typeof(Viewport), new(null));
-
-    public static readonly DependencyProperty MouseUpCommandProperty =
-        DependencyProperty.Register(nameof(MouseUpCommand), typeof(ICommand), typeof(Viewport), new(null));
-
-    private static readonly DependencyProperty BitmapsProperty =
-        DependencyProperty.Register(nameof(Bitmaps), typeof(Dictionary<ChunkResolution, WriteableBitmap>), typeof(Viewport), new(null, OnBitmapsChange));
-
-    public static readonly DependencyProperty DelayedProperty = DependencyProperty.Register(
-        nameof(Delayed), typeof(bool), typeof(Viewport), new PropertyMetadata(false));
-
-    public bool Delayed
-    {
-        get => (bool)GetValue(DelayedProperty);
-        set => SetValue(DelayedProperty, value);
-    }
-    
-    public Dictionary<ChunkResolution, WriteableBitmap>? Bitmaps
-    {
-        get => (Dictionary<ChunkResolution, WriteableBitmap>?)GetValue(BitmapsProperty);
-        set => SetValue(BitmapsProperty, value);
-    }
-
-    public ICommand? MouseDownCommand
-    {
-        get => (ICommand?)GetValue(MouseDownCommandProperty);
-        set => SetValue(MouseDownCommandProperty, value);
-    }
-
-    public ICommand? MouseMoveCommand
-    {
-        get => (ICommand?)GetValue(MouseMoveCommandProperty);
-        set => SetValue(MouseMoveCommandProperty, value);
-    }
-
-    public ICommand? MouseUpCommand
-    {
-        get => (ICommand?)GetValue(MouseUpCommandProperty);
-        set => SetValue(MouseUpCommandProperty, value);
-    }
-
-
-    public DocumentViewModel? Document
-    {
-        get => (DocumentViewModel)GetValue(DocumentProperty);
-        set => SetValue(DocumentProperty, value);
-    }
-
-    public ZoomboxMode ZoomMode
-    {
-        get => (ZoomboxMode)GetValue(ZoomModeProperty);
-        set => SetValue(ZoomModeProperty, value);
-    }
-
-    public double ZoomboxScale
-    {
-        get => zoombox.Scale;
-        // ReSharper disable once ValueParameterNotUsed
-        set
-        {
-            PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
-        }
-    }
-
-    public bool FlipX
-    {
-        get => (bool)GetValue(FlipXProperty);
-        set => SetValue(FlipXProperty, value);
-    }
-
-    public bool FlipY
-    {
-        get => (bool)GetValue(FlipYProperty);
-        set => SetValue(FlipYProperty, value);
-    }
-
-    private double angle = 0;
-
-    public double Angle
-    {
-        get => angle;
-        set
-        {
-            angle = value;
-            PropertyChanged?.Invoke(this, new(nameof(Angle)));
-            Document?.AddOrUpdateViewport(GetLocation());
-        }
-    }
-
-    private VecD center = new(32, 32);
-
-    public VecD Center
-    {
-        get => center;
-        set
-        {
-            center = value;
-            PropertyChanged?.Invoke(this, new(nameof(Center)));
-            Document?.AddOrUpdateViewport(GetLocation());
-        }
-    }
-
-    private VecD realDimensions = new(double.MaxValue, double.MaxValue);
-
-    public VecD RealDimensions
-    {
-        get => realDimensions;
-        set
-        {
-            var oldRes = CalculateResolution();
-            realDimensions = value;
-            var newRes = CalculateResolution();
-
-            PropertyChanged?.Invoke(this, new(nameof(RealDimensions)));
-            Document?.AddOrUpdateViewport(GetLocation());
-
-            if (oldRes != newRes)
-                PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
-        }
-    }
-
-    private VecD dimensions = new(64, 64);
-
-    public VecD Dimensions
-    {
-        get => dimensions;
-        set
-        {
-            var oldRes = CalculateResolution();
-            dimensions = value;
-            var newRes = CalculateResolution();
-
-            PropertyChanged?.Invoke(this, new(nameof(Dimensions)));
-            Document?.AddOrUpdateViewport(GetLocation());
-
-            if (oldRes != newRes)
-                PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
-        }
-    }
-
-    public WriteableBitmap? TargetBitmap
-    {
-        get
-        {
-            return Document?.Bitmaps.TryGetValue(CalculateResolution(), out var value) == true ? value : null;
-        }
-    }
-
-    public double ReferenceLayerScale =>
-        ZoomboxScale * ((Document?.ReferenceBitmap != null)
-            ? (Document.ReferenceShape.RectSize.X / (double)Document.ReferenceBitmap.Width)
-            : 1);
-
-    public Zoombox Zoombox => zoombox;
-
-    public Guid GuidValue { get; } = Guid.NewGuid();
-
-    public Viewport()
-    {
-        InitializeComponent();
-
-        Binding binding = new Binding { Source = this, Path = new PropertyPath("Document.Bitmaps") };
-        SetBinding(BitmapsProperty, binding);
-
-        Loaded += OnLoad;
-        Unloaded += OnUnload;
-    }
-
-    private Image? GetImage() => (Image?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1];
-
-    private void ForceRefreshFinalImage()
-    {
-        GetImage()?.InvalidateVisual();
-    }
-
-    private void OnUnload(object sender, RoutedEventArgs e)
-    {
-        Document?.RemoveViewport(GuidValue);
-    }
-
-    private void OnLoad(object sender, RoutedEventArgs e)
-    {
-        Document?.AddOrUpdateViewport(GetLocation());
-    }
-
-    private static void OnDocumentChange(DependencyObject viewportObj, DependencyPropertyChangedEventArgs args)
-    {
-        var oldDoc = (DocumentViewModel?)args.OldValue;
-        var newDoc = (DocumentViewModel?)args.NewValue;
-        var viewport = (Viewport)viewportObj;
-        oldDoc?.RemoveViewport(viewport.GuidValue);
-        newDoc?.AddOrUpdateViewport(viewport.GetLocation());
-    }
-
-    private static void OnBitmapsChange(DependencyObject viewportObj, DependencyPropertyChangedEventArgs args)
-    {
-        ((Viewport)viewportObj).PropertyChanged?.Invoke(viewportObj, new(nameof(TargetBitmap)));
-    }
-
-    private ChunkResolution CalculateResolution()
-    {
-        VecD densityVec = Dimensions.Divide(RealDimensions);
-        double density = Math.Min(densityVec.X, densityVec.Y);
-        if (density > 8.01)
-            return ChunkResolution.Eighth;
-        else if (density > 4.01)
-            return ChunkResolution.Quarter;
-        else if (density > 2.01)
-            return ChunkResolution.Half;
-        return ChunkResolution.Full;
-    }
-
-    private ViewportInfo GetLocation()
-    {
-        return new(Angle, Center, RealDimensions / 2, Dimensions / 2, CalculateResolution(), GuidValue, Delayed, ForceRefreshFinalImage);
-    }
-
-    private void OnReferenceImageSizeChanged(object? sender, SizeChangedEventArgs e)
-    {
-        PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
-    }
-}

+ 0 - 98
src/PixiEditorPrototype/ViewModels/DocumentTransformViewModel.cs

@@ -1,98 +0,0 @@
-using System;
-using System.ComponentModel;
-using ChunkyImageLib.DataHolders;
-using PixiEditorPrototype.CustomControls.TransformOverlay;
-
-namespace PixiEditorPrototype.ViewModels;
-internal class DocumentTransformViewModel : INotifyPropertyChanged
-{
-    private TransformState internalState;
-    public TransformState InternalState
-    {
-        get => internalState;
-        set
-        {
-            internalState = value;
-            PropertyChanged?.Invoke(this, new(nameof(InternalState)));
-        }
-    }
-
-    private TransformCornerFreedom cornerFreedom;
-    public TransformCornerFreedom CornerFreedom
-    {
-        get => cornerFreedom;
-        set
-        {
-            cornerFreedom = value;
-            PropertyChanged?.Invoke(this, new(nameof(CornerFreedom)));
-        }
-    }
-
-    private TransformSideFreedom sideFreedom;
-    public TransformSideFreedom SideFreedom
-    {
-        get => sideFreedom;
-        set
-        {
-            sideFreedom = value;
-            PropertyChanged?.Invoke(this, new(nameof(SideFreedom)));
-        }
-    }
-
-    private bool transformActive;
-    public bool TransformActive
-    {
-        get => transformActive;
-        set
-        {
-            transformActive = value;
-            PropertyChanged?.Invoke(this, new(nameof(TransformActive)));
-        }
-    }
-
-    private ShapeCorners requestedCorners;
-    public ShapeCorners RequestedCorners
-    {
-        get => requestedCorners;
-        set
-        {
-            requestedCorners = value;
-            PropertyChanged?.Invoke(this, new(nameof(RequestedCorners)));
-        }
-    }
-
-    private ShapeCorners corners;
-    public ShapeCorners Corners
-    {
-        get => corners;
-        set
-        {
-            corners = value;
-            PropertyChanged?.Invoke(this, new(nameof(Corners)));
-            TransformMoved?.Invoke(this, value);
-        }
-    }
-    public event PropertyChangedEventHandler? PropertyChanged;
-    public event EventHandler<ShapeCorners>? TransformMoved;
-
-    public void HideTransform()
-    {
-        TransformActive = false;
-    }
-
-    public void ShowShapeTransform(ShapeCorners initPos)
-    {
-        CornerFreedom = TransformCornerFreedom.Scale;
-        SideFreedom = TransformSideFreedom.ScaleProportionally;
-        RequestedCorners = initPos;
-        TransformActive = true;
-    }
-
-    public void ShowFreeTransform(ShapeCorners initPos)
-    {
-        CornerFreedom = TransformCornerFreedom.Free;
-        SideFreedom = TransformSideFreedom.Free;
-        RequestedCorners = initPos;
-        TransformActive = true;
-    }
-}

+ 0 - 901
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -1,901 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using ChunkyImageLib;
-using ChunkyImageLib.DataHolders;
-using ChunkyImageLib.Operations;
-using Microsoft.Win32;
-using PixiEditor.ChangeableDocument.Actions.Undo;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.Parser;
-using PixiEditorPrototype.CustomControls.SymmetryOverlay;
-using PixiEditorPrototype.Helpers;
-using PixiEditorPrototype.Models;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.ViewModels;
-
-internal class DocumentViewModel : INotifyPropertyChanged
-{
-    public event PropertyChangedEventHandler? PropertyChanged;
-
-    public RelayCommand? UndoCommand { get; }
-    public RelayCommand? RedoCommand { get; }
-    public RelayCommand? ClearSelectionCommand { get; }
-    public RelayCommand? CreateNewLayerCommand { get; }
-    public RelayCommand? CreateNewFolderCommand { get; }
-    public RelayCommand? DeleteStructureMemberCommand { get; }
-    public RelayCommand? ResizeCanvasCommand { get; }
-    public RelayCommand? ResizeImageCommand { get; }
-    public RelayCommand? CombineCommand { get; }
-    public RelayCommand? ClearHistoryCommand { get; }
-    public RelayCommand? CreateMaskCommand { get; }
-    public RelayCommand? DeleteMaskCommand { get; }
-    public RelayCommand? ApplyMaskCommand { get; }
-    public RelayCommand? ToggleLockTransparencyCommand { get; }
-    public RelayCommand? ApplyTransformCommand { get; }
-    public RelayCommand? PasteImageCommand { get; }
-    public RelayCommand? CreateReferenceLayerCommand { get; }
-    public RelayCommand? DragSymmetryCommand { get; }
-    public RelayCommand? EndDragSymmetryCommand { get; }
-    public RelayCommand? ClipToMemberBelowCommand { get; }
-    public RelayCommand? TransformSelectionPathCommand { get; }
-    public RelayCommand? TransformSelectedAreaCommand { get; }
-
-    private VecI size = new VecI(64, 64);
-
-    public void SetSize(VecI size)
-    {
-        this.size = size;
-        RaisePropertyChanged(nameof(SizeBindable));
-        RaisePropertyChanged(nameof(Width));
-        RaisePropertyChanged(nameof(Height));
-    }
-
-    public VecI SizeBindable => size;
-
-    private SKPath selectionPath = new SKPath();
-
-    public void SetSelectionPath(SKPath selectionPath)
-    {
-        (var toDispose, this.selectionPath) = (this.selectionPath, selectionPath);
-        toDispose.Dispose();
-        RaisePropertyChanged(nameof(SelectionPathBindable));
-    }
-
-    public SKPath SelectionPathBindable => selectionPath;
-
-    private int horizontalSymmetryAxisY;
-
-    public void SetHorizontalSymmetryAxisY(int horizontalSymmetryAxisY)
-    {
-        this.horizontalSymmetryAxisY = horizontalSymmetryAxisY;
-        RaisePropertyChanged(nameof(HorizontalSymmetryAxisYBindable));
-    }
-
-    public int HorizontalSymmetryAxisYBindable => horizontalSymmetryAxisY;
-
-    private int verticalSymmetryAxisX;
-
-    public void SetVerticalSymmetryAxisX(int verticalSymmetryAxisX)
-    {
-        this.verticalSymmetryAxisX = verticalSymmetryAxisX;
-        RaisePropertyChanged(nameof(VerticalSymmetryAxisXBindable));
-    }
-
-    public int VerticalSymmetryAxisXBindable => verticalSymmetryAxisX;
-
-    private bool horizontalSymmetryAxisEnabled;
-
-    public void SetHorizontalSymmetryAxisEnabled(bool horizontalSymmetryAxisEnabled)
-    {
-        this.horizontalSymmetryAxisEnabled = horizontalSymmetryAxisEnabled;
-        RaisePropertyChanged(nameof(HorizontalSymmetryAxisEnabledBindable));
-    }
-
-    public bool HorizontalSymmetryAxisEnabledBindable
-    {
-        get => horizontalSymmetryAxisEnabled;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new SymmetryAxisState_Action(SymmetryAxisDirection.Horizontal, value));
-    }
-
-    private bool verticalSymmetryAxisEnabled;
-
-    public void SetVerticalSymmetryAxisEnabled(bool verticalSymmetryAxisEnabled)
-    {
-        this.verticalSymmetryAxisEnabled = verticalSymmetryAxisEnabled;
-        RaisePropertyChanged(nameof(VerticalSymmetryAxisEnabledBindable));
-    }
-
-    public bool VerticalSymmetryAxisEnabledBindable
-    {
-        get => verticalSymmetryAxisEnabled;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new SymmetryAxisState_Action(SymmetryAxisDirection.Vertical, value));
-    }
-
-    private string name = string.Empty;
-
-    public string Name
-    {
-        get => name;
-        set
-        {
-            name = value;
-            RaisePropertyChanged(nameof(Name));
-        }
-    }
-
-    private bool busy = false;
-
-    public bool Busy
-    {
-        get => busy;
-        set
-        {
-            busy = value;
-            RaisePropertyChanged(nameof(Busy));
-        }
-    }
-
-    public StructureMemberViewModel? SelectedStructureMember => FindFirstSelectedMember();
-
-    public Guid GuidValue { get; } = Guid.NewGuid();
-    public int Width => size.X;
-    public int Height => size.Y;
-
-    public Dictionary<ChunkResolution, WriteableBitmap> Bitmaps { get; set; } = new()
-    {
-        [ChunkResolution.Full] = new WriteableBitmap(64, 64, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Half] = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Quarter] = new WriteableBitmap(16, 16, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Eighth] = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Pbgra32, null),
-    };
-
-    public WriteableBitmap PreviewBitmap { get; set; }
-    public SKSurface PreviewSurface { get; set; }
-
-    public Dictionary<ChunkResolution, SKSurface> Surfaces { get; set; } = new();
-    public FolderViewModel StructureRoot { get; }
-    public DocumentTransformViewModel TransformViewModel { get; }
-    public int ResizeWidth { get; set; } = 1024;
-    public int ResizeHeight { get; set; } = 1024;
-
-    public IReadOnlyReferenceLayer? ReferenceLayer => Helpers.Tracker.Document.ReferenceLayer;
-
-    public BitmapSource? ReferenceBitmap => ReferenceLayer?.Image.ToWriteableBitmap();
-    public VecI ReferenceBitmapSize => ReferenceLayer?.Image.Size ?? VecI.Zero;
-    public ShapeCorners ReferenceShape => ReferenceLayer?.Shape ?? default;
-
-    public Matrix ReferenceTransformMatrix
-    {
-        get
-        {
-            if (ReferenceLayer is null)
-                return Matrix.Identity;
-            var skiaMatrix = OperationHelper.CreateMatrixFromPoints(ReferenceLayer.Shape, ReferenceLayer.Image.Size);
-            return new Matrix(skiaMatrix.ScaleX, skiaMatrix.SkewY, skiaMatrix.SkewX, skiaMatrix.ScaleY, skiaMatrix.TransX, skiaMatrix.TransY);
-        }
-    }
-
-    private DocumentHelpers Helpers { get; }
-
-    private readonly ViewModelMain owner;
-
-
-    private bool updateableChangeActive = false;
-
-    private bool selectingRect = false;
-    private bool selectingEllipse = false;
-    private bool selectingLasso = false;
-    private bool drawingRectangle = false;
-    private bool drawingEllipse = false;
-    private bool drawingLine = false;
-    private bool drawingPathBasedPen = false;
-    private bool drawingLineBasedPen = false;
-    private bool drawingPixelPerfectPen = false;
-    private bool drawingBrightness = false;
-    private bool transformingRectangle = false;
-    private bool transformingEllipse = false;
-    private bool transformingReferenceLayer = false;
-    private bool shiftingLayer = false;
-
-    private bool transformingSelectionPath = false;
-    ShapeCorners initialSelectionCorners = new();
-
-    private bool pastingImage = false;
-    private Surface? pastedImage;
-
-    private ShapeCorners lastShape = new ShapeCorners();
-    private ShapeData lastShapeData = new();
-
-    private SKColor lastEllipseStrokeColor = SKColors.Empty;
-    private SKColor lastEllipseFillColor = SKColors.Empty;
-    private int lastEllipseStrokeWidth = 0;
-    private RectI lastEllipseLocation = RectI.Empty;
-
-    public DocumentViewModel(ViewModelMain owner, string name)
-    {
-        this.owner = owner;
-        Name = name;
-        TransformViewModel = new();
-        TransformViewModel.TransformMoved += OnTransformUpdate;
-
-        Helpers = new DocumentHelpers(this);
-        StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.StructureRoot.GuidValue);
-
-        UndoCommand = new RelayCommand(Undo);
-        RedoCommand = new RelayCommand(Redo);
-        ClearSelectionCommand = new RelayCommand(ClearSelection);
-        CreateNewLayerCommand = new RelayCommand(_ => Helpers.StructureHelper.CreateNewStructureMember(StructureMemberType.Layer));
-        CreateNewFolderCommand = new RelayCommand(_ => Helpers.StructureHelper.CreateNewStructureMember(StructureMemberType.Folder));
-        DeleteStructureMemberCommand = new RelayCommand(DeleteStructureMember);
-        ResizeCanvasCommand = new RelayCommand(ResizeCanvas);
-        ResizeImageCommand = new RelayCommand(ResizeImage);
-        CombineCommand = new RelayCommand(Combine);
-        ClearHistoryCommand = new RelayCommand(ClearHistory);
-        CreateMaskCommand = new RelayCommand(CreateMask);
-        DeleteMaskCommand = new RelayCommand(DeleteMask);
-        ToggleLockTransparencyCommand = new RelayCommand(ToggleLockTransparency);
-        PasteImageCommand = new RelayCommand(PasteImage);
-        CreateReferenceLayerCommand = new RelayCommand(CreateReferenceLayer);
-        ApplyTransformCommand = new RelayCommand(ApplyTransform);
-        DragSymmetryCommand = new RelayCommand(DragSymmetry);
-        EndDragSymmetryCommand = new RelayCommand(EndDragSymmetry);
-        ClipToMemberBelowCommand = new RelayCommand(ClipToMemberBelow);
-        ApplyMaskCommand = new RelayCommand(ApplyMask);
-        TransformSelectionPathCommand = new RelayCommand(TransformSelectionPath);
-        TransformSelectedAreaCommand = new RelayCommand(TransformSelectedArea);
-
-        foreach (var bitmap in Bitmaps)
-        {
-            var surface = SKSurface.Create(
-                new SKImageInfo(bitmap.Value.PixelWidth, bitmap.Value.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
-                bitmap.Value.BackBuffer, bitmap.Value.BackBufferStride);
-            Surfaces[bitmap.Key] = surface;
-        }
-
-        var previewSize = StructureMemberViewModel.CalculatePreviewSize(SizeBindable);
-        PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
-        PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
-    }
-
-    private void ResizeImage(object? obj)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new ResizeImage_Action(new(ResizeWidth, ResizeHeight), ResamplingMethod.NearestNeighbor));
-    }
-
-    public static DocumentViewModel FromSerializableDocument(ViewModelMain owner, SerializableDocument serDocument, string name)
-    {
-        DocumentViewModel document = new DocumentViewModel(owner, name);
-        var acc = document.Helpers.ActionAccumulator;
-        acc.AddActions(new ResizeCanvas_Action(new(serDocument.Width, serDocument.Height), ResizeAnchor.TopLeft));
-        int index = 0;
-        foreach (var layer in serDocument.Layers.Reverse())
-        {
-            var guid = Guid.NewGuid();
-            var png = SKBitmap.Decode(layer.PngBytes);
-            Surface surface = new(new VecI(layer.Width, layer.Height));
-            surface.SkiaSurface.Canvas.DrawBitmap(png, 0, 0);
-            acc.AddFinishedActions(
-                new CreateStructureMember_Action(document.StructureRoot.GuidValue, guid, index, StructureMemberType.Layer),
-                new StructureMemberName_Action(guid, layer.Name),
-                new PasteImage_Action(surface, new(new RectD(new VecD(layer.OffsetX, layer.OffsetY), new(layer.Width, layer.Height))), guid, true, false),
-                new EndPasteImage_Action()
-            );
-            if (layer.Opacity != 1)
-            {
-                acc.AddFinishedActions(
-                    new StructureMemberOpacity_Action(guid, layer.Opacity),
-                    new EndStructureMemberOpacity_Action());
-            }
-
-            if (!layer.IsVisible)
-                acc.AddFinishedActions(new StructureMemberIsVisible_Action(layer.IsVisible, guid));
-        }
-
-        acc.AddActions(new DeleteRecordedChanges_Action());
-        return document;
-    }
-
-    public StructureMemberViewModel? FindFirstSelectedMember() => Helpers.StructureHelper.FindFirstWhere(member => member.IsSelected);
-
-    private bool CanStartUpdate()
-    {
-        var member = FindFirstSelectedMember();
-        if (member is null)
-            return false;
-        bool drawOnMask = member.ShouldDrawOnMask;
-        if (!drawOnMask)
-        {
-            if (member is FolderViewModel)
-                return false;
-            if (member is LayerViewModel)
-                return true;
-        }
-
-        if (!member.HasMaskBindable)
-            return false;
-        return true;
-    }
-
-    private void TransformSelectedArea(object? obj)
-    {
-        if (updateableChangeActive || FindFirstSelectedMember() is not LayerViewModel layer || SelectionPathBindable.IsEmpty)
-            return;
-        IReadOnlyChunkyImage? layerImage = (Helpers.Tracker.Document.FindMember(layer.GuidValue) as IReadOnlyLayer)?.LayerImage;
-        if (layerImage is null)
-            return;
-
-        // find area location and size
-        using SKPath path = new(SelectionPathBindable);
-        var bounds = (RectD)path.TightBounds;
-        bounds = bounds.Intersect(new RectD(VecD.Zero, SizeBindable));
-        var intBounds = (RectI)bounds.RoundOutwards();
-
-        // extract surface to be transformed
-        path.Transform(SKMatrix.CreateTranslation(-intBounds.X, -intBounds.Y));
-        Surface surface = new(intBounds.Size);
-        surface.SkiaSurface.Canvas.Save();
-        surface.SkiaSurface.Canvas.ClipPath(path);
-        layerImage.DrawMostUpToDateRegionOn(intBounds, ChunkResolution.Full, surface.SkiaSurface, VecI.Zero);
-        surface.SkiaSurface.Canvas.Restore();
-
-        // clear area
-        if (!owner.KeepOriginalImageOnTransform)
-        {
-            Helpers.ActionAccumulator.AddActions(new ClearSelectedArea_Action(layer.GuidValue, false));
-        }
-
-        Helpers.ActionAccumulator.AddActions(new ClearSelection_Action());
-
-        // initiate transform using paste image logic
-        pastedImage = surface;
-        pastingImage = true;
-        ShapeCorners corners = new(intBounds);
-        Helpers.ActionAccumulator.AddActions(new PasteImage_Action(pastedImage, corners, layer.GuidValue, true, false));
-        TransformViewModel.ShowFreeTransform(corners);
-    }
-
-    private void ApplyMask(object? obj)
-    {
-        if (updateableChangeActive || FindFirstSelectedMember() is not LayerViewModel layer || !layer.HasMaskBindable)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new ApplyLayerMask_Action(layer.GuidValue));
-    }
-
-    private void ClipToMemberBelow(object? obj)
-    {
-        if (updateableChangeActive || FindFirstSelectedMember() is not { } member)
-            return;
-        member.ClipToMemberBelowEnabledBindable = !member.ClipToMemberBelowEnabledBindable;
-    }
-
-    private void DragSymmetry(object? obj)
-    {
-        if (obj is null)
-            return;
-        var info = (SymmetryAxisDragInfo)obj;
-        Helpers.ActionAccumulator.AddActions(new SymmetryAxisPosition_Action(info.Direction, info.NewPosition));
-    }
-
-    private void EndDragSymmetry(object? obj)
-    {
-        if (obj is null)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndSymmetryAxisPosition_Action());
-    }
-
-    public void StartUpdatePathBasedPen(VecD pos)
-    {
-        if (!CanStartUpdate())
-            return;
-        updateableChangeActive = true;
-        drawingPathBasedPen = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(new PathBasedPen_Action(
-            member!.GuidValue,
-            pos,
-            new SKColor(owner.SelectedColor.R, owner.SelectedColor.G, owner.SelectedColor.B, owner.SelectedColor.A),
-            owner.StrokeWidth,
-            member.ShouldDrawOnMask));
-    }
-
-    public void EndPathBasedPen()
-    {
-        if (!drawingPathBasedPen)
-            return;
-        drawingPathBasedPen = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndPathBasedPen_Action());
-    }
-
-    public void StartUpdateBrightness(VecI pos, int strokeWidth, float correctionFactor, bool repeat, bool darken)
-    {
-        if (!CanStartUpdate())
-            return;
-        updateableChangeActive = true;
-        drawingBrightness = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(
-            new ChangeBrightness_Action(
-                member!.GuidValue,
-                pos,
-                correctionFactor,
-                strokeWidth,
-                repeat,
-                darken));
-    }
-
-    public void EndBrightness()
-    {
-        if (!drawingBrightness)
-            return;
-        drawingBrightness = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndChangeBrightness_Action());
-    }
-
-    public void StartUpdateLineBasedPen(VecI pos, SKColor color, bool replacing = false)
-    {
-        if (!CanStartUpdate())
-            return;
-        updateableChangeActive = true;
-        drawingLineBasedPen = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(new LineBasedPen_Action(
-            member!.GuidValue,
-            color,
-            pos,
-            (int)owner.StrokeWidth,
-            replacing,
-            member.ShouldDrawOnMask));
-    }
-
-    public void EndLineBasedPen()
-    {
-        if (!drawingLineBasedPen)
-            return;
-        drawingLineBasedPen = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndLineBasedPen_Action());
-    }
-
-    public void StartUpdatePixelPerfectPen(VecI pos, SKColor color)
-    {
-        if (!CanStartUpdate())
-            return;
-        updateableChangeActive = true;
-        drawingPixelPerfectPen = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(new PixelPerfectPen_Action(member!.GuidValue, pos, color, member.ShouldDrawOnMask));
-    }
-
-    public void EndUPixelPerfectPen()
-    {
-        if (!drawingPixelPerfectPen)
-            return;
-        drawingPixelPerfectPen = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndPixelPerfectPen_Action());
-    }
-
-    public void StartUpdateLine(VecI from, VecI to, SKColor color, SKStrokeCap cap, int strokeWidth)
-    {
-        if (!CanStartUpdate())
-            return;
-        drawingLine = true;
-        updateableChangeActive = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(
-            new DrawLine_Action(member!.GuidValue, from, to, strokeWidth, color, cap, member.ShouldDrawOnMask));
-    }
-
-    public void EndLine()
-    {
-        if (!drawingLine)
-            return;
-        drawingLine = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndDrawLine_Action());
-    }
-
-    public void StartUpdateEllipse(RectI location, SKColor strokeColor, SKColor fillColor, int strokeWidth)
-    {
-        if (!CanStartUpdate())
-            return;
-        drawingEllipse = true;
-        updateableChangeActive = true;
-        lastEllipseFillColor = fillColor;
-        lastEllipseStrokeWidth = strokeWidth;
-        lastEllipseStrokeColor = strokeColor;
-        lastEllipseLocation = location;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(new DrawEllipse_Action(
-            member!.GuidValue,
-            location,
-            strokeColor,
-            fillColor,
-            strokeWidth,
-            member.ShouldDrawOnMask));
-    }
-
-    public void EndEllipse()
-    {
-        if (!drawingEllipse)
-            return;
-        drawingEllipse = false;
-        TransformViewModel.ShowShapeTransform(new ShapeCorners(lastEllipseLocation));
-        transformingEllipse = true;
-    }
-
-    public void StartUpdateRectangle(ShapeData data)
-    {
-        if (!CanStartUpdate())
-            return;
-        updateableChangeActive = true;
-        drawingRectangle = true;
-        var member = FindFirstSelectedMember();
-        Helpers.ActionAccumulator.AddActions(new DrawRectangle_Action(member!.GuidValue, data, member.ShouldDrawOnMask));
-        lastShape = new ShapeCorners(data.Center, data.Size);
-        lastShapeData = data;
-    }
-
-    public void EndRectangleDrawing()
-    {
-        if (!drawingRectangle)
-            return;
-        drawingRectangle = false;
-
-        TransformViewModel.ShowShapeTransform(lastShape);
-        transformingRectangle = true;
-    }
-
-    public void StartUpdateShiftLayer(VecI delta)
-    {
-        if (FindFirstSelectedMember() is not LayerViewModel layer)
-            return;
-        updateableChangeActive = true;
-        shiftingLayer = true;
-        Helpers.ActionAccumulator.AddActions(new ShiftLayer_Action(layer.GuidValue, delta, false));
-    }
-
-    public void EndShiftLayer()
-    {
-        if (!shiftingLayer)
-            return;
-        updateableChangeActive = false;
-        shiftingLayer = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndShiftLayer_Action());
-    }
-
-    private void TransformSelectionPath(object? arg)
-    {
-        var path = SelectionPathBindable;
-        if (path.IsEmpty)
-            return;
-        updateableChangeActive = true;
-        transformingSelectionPath = true;
-        var bounds = path.TightBounds;
-        initialSelectionCorners = new ShapeCorners(bounds);
-        TransformViewModel.ShowShapeTransform(initialSelectionCorners);
-        Helpers.ActionAccumulator.AddActions(new TransformSelectionPath_Action(initialSelectionCorners));
-    }
-
-    public void StartUpdateLassoSelection(VecI startPos, SelectionMode mode)
-    {
-        updateableChangeActive = true;
-        selectingLasso = true;
-        Helpers.ActionAccumulator.AddActions(new SelectLasso_Action(startPos, mode));
-    }
-
-    public void EndLassoSelection()
-    {
-        if (!selectingLasso)
-            return;
-        selectingLasso = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndSelectLasso_Action());
-    }
-
-    public SKColor PickColor(VecI pos, bool fromAllLayers)
-    {
-        // there is a tiny chance that the image might get disposed by another thread
-        try
-        {
-            // it might've been a better idea to implement this function
-            // via a passthrough action to avoid all the try catches
-            if (fromAllLayers)
-            {
-                VecI chunkPos = OperationHelper.GetChunkPos(pos, ChunkyImage.FullChunkSize);
-                return ChunkRenderer.MergeWholeStructure(chunkPos, ChunkResolution.Full, Helpers.Tracker.Document.StructureRoot)
-                    .Match<SKColor>(
-                        (Chunk chunk) =>
-                        {
-                            VecI posOnChunk = pos - chunkPos * ChunkyImage.FullChunkSize;
-                            var color = chunk.Surface.GetSRGBPixel(posOnChunk);
-                            chunk.Dispose();
-                            return color;
-                        },
-                        _ => SKColors.Transparent
-                    );
-            }
-
-            if (SelectedStructureMember is not LayerViewModel layerVm)
-                return SKColors.Transparent;
-            var maybeMember = Helpers.Tracker.Document.FindMember(layerVm.GuidValue);
-            if (maybeMember is not IReadOnlyLayer layer)
-                return SKColors.Transparent;
-            return layer.LayerImage.GetMostUpToDatePixel(pos);
-        }
-        catch (ObjectDisposedException)
-        {
-            return SKColors.Transparent;
-        }
-    }
-
-    private void ApplyTransform(object? param)
-    {
-        if (!transformingRectangle && !pastingImage && !transformingSelectionPath && !transformingEllipse && !transformingReferenceLayer)
-            return;
-
-        if (transformingRectangle)
-        {
-            transformingRectangle = false;
-            TransformViewModel.HideTransform();
-            Helpers.ActionAccumulator.AddFinishedActions(new EndDrawRectangle_Action());
-        }
-        else if (transformingEllipse)
-        {
-            transformingEllipse = false;
-            TransformViewModel.HideTransform();
-            Helpers.ActionAccumulator.AddFinishedActions(new EndDrawEllipse_Action());
-        }
-        else if (pastingImage)
-        {
-            pastingImage = false;
-            TransformViewModel.HideTransform();
-            Helpers.ActionAccumulator.AddFinishedActions(new EndPasteImage_Action());
-            pastedImage?.Dispose();
-            pastedImage = null;
-        }
-        else if (transformingSelectionPath)
-        {
-            transformingSelectionPath = false;
-            TransformViewModel.HideTransform();
-            Helpers.ActionAccumulator.AddFinishedActions(new EndTransformSelectionPath_Action());
-        }
-        else if (transformingReferenceLayer)
-        {
-            transformingReferenceLayer = false;
-            TransformViewModel.HideTransform();
-            Helpers.ActionAccumulator.AddFinishedActions(new EndCreateReferenceLayer_Action());
-        }
-        updateableChangeActive = false;
-    }
-
-    public void StartUpdateRectSelection(RectI rect, SelectionMode mode)
-    {
-        selectingRect = true;
-        updateableChangeActive = true;
-        Helpers.ActionAccumulator.AddActions(new SelectRectangle_Action(rect, mode));
-    }
-
-    public void EndRectSelection()
-    {
-        if (!selectingRect)
-            return;
-        selectingRect = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndSelectRectangle_Action());
-    }
-
-    public void StartUpdateEllipseSelection(RectI borders, SelectionMode mode)
-    {
-        selectingEllipse = true;
-        updateableChangeActive = true;
-        Helpers.ActionAccumulator.AddActions(new SelectEllipse_Action(borders, mode));
-    }
-
-    public void EndEllipseSelection()
-    {
-        if (!selectingEllipse)
-            return;
-        selectingEllipse = false;
-        updateableChangeActive = false;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndSelectEllipse_Action());
-    }
-
-    private void OnTransformUpdate(object? sender, ShapeCorners newCorners)
-    {
-        if (transformingRectangle)
-        {
-            StartUpdateRectangle(new ShapeData(
-                newCorners.RectCenter,
-                newCorners.RectSize,
-                newCorners.RectRotation,
-                lastShapeData.StrokeWidth,
-                lastShapeData.StrokeColor,
-                lastShapeData.FillColor,
-                lastShapeData.BlendMode));
-        }
-        else if (transformingEllipse)
-        {
-            StartUpdateEllipse(RectI.FromTwoPoints((VecI)newCorners.TopLeft, (VecI)newCorners.BottomRight), lastEllipseStrokeColor, lastEllipseFillColor, lastEllipseStrokeWidth);
-        }
-        else if (pastingImage)
-        {
-            var member = FindFirstSelectedMember();
-            if (member is null || pastedImage is null)
-                return;
-            Helpers.ActionAccumulator.AddActions(new PasteImage_Action(pastedImage, newCorners, member.GuidValue, false, false));
-        }
-        else if (transformingSelectionPath)
-        {
-            Helpers.ActionAccumulator.AddActions(new TransformSelectionPath_Action(newCorners));
-        }
-        else if (transformingReferenceLayer)
-        {
-            Helpers.ActionAccumulator.AddActions(new CreateReferenceLayer_Action(null, newCorners));
-        }
-    }
-
-    public void FloodFill(VecI pos, SKColor color, bool referenceAllLayers)
-    {
-        var member = FindFirstSelectedMember();
-        if (updateableChangeActive || member is null)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new FloodFill_Action(member.GuidValue, pos, color, referenceAllLayers, member.ShouldDrawOnMask));
-    }
-
-    public void AddOrUpdateViewport(ViewportInfo info)
-    {
-        Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
-    }
-
-    public void RemoveViewport(Guid viewportGuid)
-    {
-        Helpers.ActionAccumulator.AddActions(new RemoveViewport_PassthroughAction(viewportGuid));
-    }
-
-    private void PasteImage(object? args)
-    {
-        if (FindFirstSelectedMember() is not LayerViewModel layer)
-            return;
-        OpenFileDialog dialog = new();
-        if (dialog.ShowDialog() != true)
-            return;
-
-        pastedImage = Surface.Load(dialog.FileName);
-        pastingImage = true;
-        ShapeCorners corners = new ShapeCorners(new RectD(VecD.Zero, pastedImage.Size));
-        Helpers.ActionAccumulator.AddActions(new PasteImage_Action(pastedImage, corners, layer.GuidValue, false, false));
-        TransformViewModel.ShowFreeTransform(corners);
-    }
-
-    private void CreateReferenceLayer(object? args)
-    {
-        OpenFileDialog dialog = new();
-        if (dialog.ShowDialog() != true)
-            return;
-
-        var referenceImage = Surface.Load(dialog.FileName);
-
-        RectD referenceImageRect = new RectD(VecD.Zero, SizeBindable).AspectFit(new RectD(VecD.Zero, referenceImage.Size));
-        ShapeCorners corners = new ShapeCorners(referenceImageRect);
-
-        Helpers.ActionAccumulator.AddActions(new CreateReferenceLayer_Action(referenceImage, corners));
-        transformingReferenceLayer = true;
-        TransformViewModel.ShowShapeTransform(corners);
-    }
-
-    private void ClearSelection(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new ClearSelection_Action());
-    }
-
-    private void DeleteStructureMember(object? param)
-    {
-        if (updateableChangeActive || FindFirstSelectedMember() is not { } member)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new DeleteStructureMember_Action(member.GuidValue));
-    }
-
-    private void Undo(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddActions(new Undo_Action());
-    }
-
-    private void Redo(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddActions(new Redo_Action());
-    }
-
-    private void ToggleLockTransparency(object? param)
-    {
-        if (updateableChangeActive || FindFirstSelectedMember() is not LayerViewModel layer)
-            return;
-        layer.LockTransparencyBindable = !layer.LockTransparencyBindable;
-    }
-
-    private void ResizeCanvas(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new ResizeCanvas_Action(new(ResizeWidth, ResizeHeight), owner.ResizeAnchor));
-    }
-
-    private void CreateMask(object? param)
-    {
-        var member = FindFirstSelectedMember();
-        if (updateableChangeActive || member is null || member.HasMaskBindable)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMemberMask_Action(member.GuidValue));
-    }
-
-    private void DeleteMask(object? param)
-    {
-        var member = FindFirstSelectedMember();
-        if (updateableChangeActive || member is null || !member.HasMaskBindable)
-            return;
-        Helpers.ActionAccumulator.AddFinishedActions(new DeleteStructureMemberMask_Action(member.GuidValue));
-    }
-
-    private void Combine(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        List<Guid> selected = new();
-        AddSelectedMembers(StructureRoot, selected);
-        if (selected.Count < 2)
-            return;
-
-        var (child, parent) = Helpers.StructureHelper.FindChildAndParentOrThrow(selected[0]);
-        int index = parent.Children.IndexOf(child);
-        Guid newGuid = Guid.NewGuid();
-
-        //make a new layer, put combined image onto it, delete layers that were merged
-        Helpers.ActionAccumulator.AddActions(
-            new CreateStructureMember_Action(parent.GuidValue, newGuid, index, StructureMemberType.Layer),
-            new StructureMemberName_Action(newGuid, child.NameBindable + "-comb"),
-            new CombineStructureMembersOnto_Action(selected.ToHashSet(), newGuid));
-        foreach (var member in selected)
-            Helpers.ActionAccumulator.AddActions(new DeleteStructureMember_Action(member));
-        Helpers.ActionAccumulator.AddActions(new ChangeBoundary_Action());
-    }
-
-    private void ClearHistory(object? param)
-    {
-        if (updateableChangeActive)
-            return;
-        Helpers.ActionAccumulator.AddActions(new DeleteRecordedChanges_Action());
-    }
-
-    private void AddSelectedMembers(FolderViewModel folder, List<Guid> collection)
-    {
-        foreach (var child in folder.Children)
-        {
-            if (child.IsSelected)
-                collection.Add(child.GuidValue);
-            if (child is FolderViewModel innerFolder)
-                AddSelectedMembers(innerFolder, collection);
-        }
-    }
-
-    public void RaisePropertyChanged(string name)
-    {
-        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-    }
-}

+ 0 - 11
src/PixiEditorPrototype/ViewModels/FolderViewModel.cs

@@ -1,11 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-using PixiEditorPrototype.Models;
-
-namespace PixiEditorPrototype.ViewModels;
-
-internal class FolderViewModel : StructureMemberViewModel
-{
-    public ObservableCollection<StructureMemberViewModel> Children { get; } = new();
-    public FolderViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue) : base(doc, helpers, guidValue) { }
-}

+ 0 - 22
src/PixiEditorPrototype/ViewModels/LayerViewModel.cs

@@ -1,22 +0,0 @@
-using System;
-using PixiEditorPrototype.Models;
-
-namespace PixiEditorPrototype.ViewModels;
-
-internal class LayerViewModel : StructureMemberViewModel
-{
-    bool lockTransparency;
-    public void SetLockTransparency(bool lockTransparency)
-    {
-        this.lockTransparency = lockTransparency;
-        RaisePropertyChanged(nameof(LockTransparencyBindable));
-    }
-    public bool LockTransparencyBindable
-    {
-        get => lockTransparency;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(GuidValue, value));
-    }
-    public LayerViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue) : base(doc, helpers, guidValue)
-    {
-    }
-}

+ 0 - 174
src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs

@@ -1,174 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditorPrototype.Models;
-using SkiaSharp;
-
-namespace PixiEditorPrototype.ViewModels;
-#nullable enable
-internal abstract class StructureMemberViewModel : INotifyPropertyChanged
-{
-    public event PropertyChangedEventHandler? PropertyChanged;
-    protected DocumentViewModel Document { get; }
-    protected DocumentHelpers Helpers { get; }
-
-
-    private string name = "";
-    public void SetName(string name)
-    {
-        this.name = name;
-        RaisePropertyChanged(nameof(NameBindable));
-    }
-    public string NameBindable
-    {
-        get => name;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(GuidValue, value));
-    }
-
-    private bool isVisible;
-    public void SetIsVisible(bool isVisible)
-    {
-        this.isVisible = isVisible;
-        RaisePropertyChanged(nameof(IsVisibleBindable));
-    }
-    public bool IsVisibleBindable
-    {
-        get => isVisible;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberIsVisible_Action(value, GuidValue));
-    }
-
-    private bool maskIsVisible;
-    public void SetMaskIsVisible(bool maskIsVisible)
-    {
-        this.maskIsVisible = maskIsVisible;
-        RaisePropertyChanged(nameof(MaskIsVisibleBindable));
-    }
-    public bool MaskIsVisibleBindable
-    {
-        get => maskIsVisible;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberMaskIsVisible_Action(value, GuidValue));
-    }
-
-    private BlendMode blendMode;
-    public void SetBlendMode(BlendMode blendMode)
-    {
-        this.blendMode = blendMode;
-        RaisePropertyChanged(nameof(BlendModeBindable));
-    }
-    public BlendMode BlendModeBindable
-    {
-        get => blendMode;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberBlendMode_Action(value, GuidValue));
-    }
-
-    private bool clipToMemberBelowEnabled;
-    public void SetClipToMemberBelowEnabled(bool clipToMemberBelowEnabled)
-    {
-        this.clipToMemberBelowEnabled = clipToMemberBelowEnabled;
-        RaisePropertyChanged(nameof(ClipToMemberBelowEnabledBindable));
-    }
-    public bool ClipToMemberBelowEnabledBindable
-    {
-        get => clipToMemberBelowEnabled;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberClipToMemberBelow_Action(value, GuidValue));
-    }
-
-    private bool hasMask;
-    public void SetHasMask(bool hasMask)
-    {
-        this.hasMask = hasMask;
-        RaisePropertyChanged(nameof(HasMaskBindable));
-    }
-    public bool HasMaskBindable
-    {
-        get => hasMask;
-    }
-
-    private Guid guidValue;
-    public Guid GuidValue
-    {
-        get => guidValue;
-    }
-
-    private float opacity;
-
-    public void SetOpacity(float opacity)
-    {
-        this.opacity = opacity;
-        RaisePropertyChanged(nameof(OpacityBindable));
-    }
-    public float OpacityBindable
-    {
-        get => opacity;
-    }
-
-    private bool isSelected;
-    public bool IsSelected
-    {
-        get => isSelected;
-        set
-        {
-            isSelected = value;
-            Document.RaisePropertyChanged(nameof(Document.SelectedStructureMember));
-        }
-    }
-    public bool ShouldDrawOnMask { get; set; }
-
-
-    public const int PreviewSize = 48;
-    public WriteableBitmap PreviewBitmap { get; set; }
-    public SKSurface PreviewSurface { get; set; }
-
-    public WriteableBitmap? MaskPreviewBitmap { get; set; }
-    public SKSurface? MaskPreviewSurface { get; set; }
-
-    public RelayCommand MoveUpCommand { get; }
-    public RelayCommand MoveDownCommand { get; }
-    public RelayCommand UpdateOpacityCommand { get; }
-    public RelayCommand EndOpacityUpdateCommand { get; }
-
-    public void RaisePropertyChanged(string name)
-    {
-        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-    }
-
-    public static VecI CalculatePreviewSize(VecI docSize)
-    {
-        double proportions = docSize.Y / (double)docSize.X;
-        const int prSize = StructureMemberViewModel.PreviewSize;
-        return proportions > 1 ?
-            new VecI((int)Math.Round(prSize / proportions), prSize) :
-            new VecI(prSize, (int)Math.Round(prSize * proportions));
-    }
-
-    public StructureMemberViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue)
-    {
-        Document = doc;
-        Helpers = helpers;
-
-        MoveUpCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, false));
-        MoveDownCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, true));
-        UpdateOpacityCommand = new(UpdateOpacity);
-        EndOpacityUpdateCommand = new(EndOpacityUpdate);
-
-        this.guidValue = guidValue;
-        var previewSize = CalculatePreviewSize(doc.SizeBindable);
-        PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
-        PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
-    }
-
-    private void EndOpacityUpdate(object? opacity)
-    {
-        Helpers.ActionAccumulator.AddFinishedActions(new EndStructureMemberOpacity_Action());
-    }
-
-    private void UpdateOpacity(object? opacity)
-    {
-        if (opacity is not double value)
-            throw new ArgumentException("The passed value isn't a double");
-        Helpers.ActionAccumulator.AddActions(new StructureMemberOpacity_Action(GuidValue, (float)value));
-    }
-}

+ 0 - 367
src/PixiEditorPrototype/ViewModels/ViewModelMain.cs

@@ -1,367 +0,0 @@
-using System;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.IO;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Media;
-using ChunkyImageLib.DataHolders;
-using Microsoft.Win32;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.Parser;
-using PixiEditor.Zoombox;
-using PixiEditorPrototype.Models;
-using SkiaSharp;
-using SelectionMode = PixiEditor.ChangeableDocument.Enums.SelectionMode;
-
-namespace PixiEditorPrototype.ViewModels;
-
-internal class ViewModelMain : INotifyPropertyChanged
-{
-    public DocumentViewModel? ActiveDocument => ActiveDocumentIndex >= 0 && ActiveDocumentIndex < Documents.Count ? Documents[ActiveDocumentIndex] : null;
-
-    public RelayCommand MouseDownCommand { get; }
-    public RelayCommand MouseMoveCommand { get; }
-    public RelayCommand MouseUpCommand { get; }
-    public RelayCommand ChangeActiveToolCommand { get; }
-    public RelayCommand SetSelectionModeCommand { get; }
-    public RelayCommand SetLineCapCommand { get; }
-    public RelayCommand SetResizeAnchorCommand { get; }
-    public RelayCommand LoadDocumentCommand { get; }
-
-    public bool KeepOriginalImageOnTransform { get; set; } = false;
-    public bool ReferenceAllLayers { get; set; } = false;
-    public bool BrightnessDarken { get; set; } = false;
-    public bool BrightnessRepeat { get; set; } = false;
-    public float BrightnessCorrectionFactor { get; set; } = 5f;
-    public float StrokeWidth { get; set; } = 1f;
-    public SKStrokeCap LineStrokeCap { get; set; } = SKStrokeCap.Butt;
-
-    public event PropertyChangedEventHandler? PropertyChanged;
-
-    private Color selectedColor = Colors.Black;
-    public Color SelectedColor
-    {
-        get => selectedColor;
-        set
-        {
-            selectedColor = value;
-            PropertyChanged?.Invoke(this, new(nameof(SelectedColor)));
-        }
-    }
-
-    public bool NormalZoombox
-    {
-        set
-        {
-            if (!value)
-                return;
-            ZoomboxMode = ZoomboxMode.Normal;
-            PropertyChanged?.Invoke(this, new(nameof(ZoomboxMode)));
-        }
-    }
-
-    public bool MoveZoombox
-    {
-        set
-        {
-            if (!value)
-                return;
-            ZoomboxMode = ZoomboxMode.Move;
-            PropertyChanged?.Invoke(this, new(nameof(ZoomboxMode)));
-        }
-    }
-
-    public bool RotateZoombox
-    {
-        set
-        {
-            if (!value)
-                return;
-            ZoomboxMode = ZoomboxMode.Rotate;
-            PropertyChanged?.Invoke(this, new(nameof(ZoomboxMode)));
-        }
-    }
-
-    private int activeDocumentIndex;
-
-    public int ActiveDocumentIndex
-    {
-        get => activeDocumentIndex;
-        set
-        {
-            activeDocumentIndex = value;
-            PropertyChanged?.Invoke(this, new(nameof(ActiveDocumentIndex)));
-            PropertyChanged?.Invoke(this, new(nameof(ActiveDocument)));
-        }
-    }
-
-    public ZoomboxMode ZoomboxMode { get; set; }
-
-    public ObservableCollection<DocumentViewModel> Documents { get; } = new();
-
-
-    private VecD mouseDownCanvasPos;
-
-    private bool mouseHasMoved = false;
-    private bool mouseIsDown = false;
-
-    private Tool activeTool = Tool.Rectangle;
-    private SelectionMode selectionMode = SelectionMode.New;
-    public ResizeAnchor ResizeAnchor { get; private set; } = ResizeAnchor.TopLeft;
-
-    private Tool toolOnMouseDown = Tool.Rectangle;
-
-    public ViewModelMain()
-    {
-        MouseDownCommand = new RelayCommand(MouseDown);
-        MouseMoveCommand = new RelayCommand(MouseMove);
-        MouseUpCommand = new RelayCommand(MouseUp);
-        ChangeActiveToolCommand = new RelayCommand(ChangeActiveTool);
-        SetSelectionModeCommand = new RelayCommand(SetSelectionMode);
-        SetLineCapCommand = new RelayCommand(SetLineCap);
-        SetResizeAnchorCommand = new RelayCommand(SetResizeAnchor);
-        LoadDocumentCommand = new RelayCommand(LoadDocument);
-
-        Documents.Add(new DocumentViewModel(this, "New Artwork"));
-    }
-
-    private void SetLineCap(object? obj)
-    {
-        if (obj is not SKStrokeCap cap)
-            return;
-        LineStrokeCap = cap;
-    }
-
-    private void SetResizeAnchor(object? obj)
-    {
-        if (obj is not ResizeAnchor anchor)
-            return;
-        ResizeAnchor = anchor;
-    }
-
-    private void SetSelectionMode(object? obj)
-    {
-        if (obj is not SelectionMode mode)
-            return;
-        selectionMode = mode;
-    }
-
-    private void MouseDown(object? param)
-    {
-        if (ActiveDocument is null || ZoomboxMode != ZoomboxMode.Normal || ActiveDocument.TransformViewModel.TransformActive)
-            return;
-        if (mouseIsDown)
-        {
-            mouseIsDown = false;
-            ProcessToolMouseUp();
-            mouseHasMoved = false;
-        }
-
-        mouseIsDown = true;
-        var args = (MouseButtonEventArgs)(param!);
-        var source = (Image)args.Source;
-        var pos = args.GetPosition(source);
-        mouseDownCanvasPos = new() { X = pos.X / source.Width * ActiveDocument.Bitmaps[ChunkResolution.Full].PixelWidth, Y = pos.Y / source.Height * ActiveDocument.Bitmaps[ChunkResolution.Full].PixelHeight };
-        toolOnMouseDown = activeTool;
-        ProcessToolMouseDown(mouseDownCanvasPos);
-    }
-
-    private void ProcessToolMouseDown(VecD pos)
-    {
-        switch (toolOnMouseDown)
-        {
-            case Tool.FloodFill:
-                ActiveDocument!.FloodFill((VecI)pos, new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A), ReferenceAllLayers);
-                break;
-            case Tool.PathBasedPen:
-                ActiveDocument!.StartUpdatePathBasedPen(pos);
-                break;
-            case Tool.LineBasedPen:
-                ActiveDocument!.StartUpdateLineBasedPen((VecI)pos, new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A));
-                break;
-            case Tool.PixelPerfectPen:
-                ActiveDocument!.StartUpdatePixelPerfectPen((VecI)pos, new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A));
-                break;
-            case Tool.Brightness:
-                ActiveDocument!.StartUpdateBrightness((VecI)pos, (int)StrokeWidth, BrightnessCorrectionFactor, BrightnessRepeat, BrightnessDarken);
-                break;
-            case Tool.Eraser:
-                ActiveDocument!.StartUpdateLineBasedPen((VecI)pos, SKColors.Transparent, true);
-                break;
-            case Tool.Pipette:
-                var color = ActiveDocument!.PickColor((VecI)pos, ReferenceAllLayers);
-                SelectedColor = Color.FromArgb(color.Alpha, color.Red, color.Green, color.Blue);
-                break;
-        }
-    }
-
-    private void MouseMove(object? param)
-    {
-        if (!mouseIsDown)
-            return;
-        mouseHasMoved = true;
-        var args = (MouseEventArgs)(param!);
-        var source = (Image)args.Source;
-        var pos = args.GetPosition(source);
-        double curX = pos.X / source.Width * ActiveDocument!.Bitmaps[ChunkResolution.Full].PixelWidth;
-        double curY = pos.Y / source.Height * ActiveDocument.Bitmaps[ChunkResolution.Full].PixelHeight;
-
-        ProcessToolMouseMove(new VecD(curX, curY));
-    }
-
-    private void ProcessToolMouseMove(VecD canvasPos)
-    {
-        switch (toolOnMouseDown)
-        {
-            case Tool.Rectangle:
-                {
-                    var rect = RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos);
-                    ActiveDocument!.StartUpdateRectangle(new ShapeData(
-                        rect.Center,
-                        rect.Size,
-                        0,
-                        (int)StrokeWidth,
-                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                        new SKColor(0, 0, 255, 128)));
-                    break;
-                }
-            case Tool.Ellipse:
-                {
-                    ActiveDocument!.StartUpdateEllipse(
-                        RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),
-                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                        new SKColor(0, 0, 255, 128),
-                        (int)StrokeWidth);
-                    break;
-                }
-            case Tool.Line:
-                {
-                    ActiveDocument!.StartUpdateLine(
-                        (VecI)mouseDownCanvasPos, (VecI)canvasPos,
-                        new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A),
-                        LineStrokeCap,
-                        (int)StrokeWidth);
-                    break;
-                }
-            case Tool.SelectRectangle:
-                ActiveDocument!.StartUpdateRectSelection(
-                    RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),
-                    selectionMode);
-                break;
-            case Tool.SelectEllipse:
-                ActiveDocument!.StartUpdateEllipseSelection(
-                    RectI.FromTwoPixels((VecI)mouseDownCanvasPos, (VecI)canvasPos),
-                    selectionMode);
-                break;
-            case Tool.ShiftLayer:
-                ActiveDocument!.StartUpdateShiftLayer((VecI)canvasPos - (VecI)mouseDownCanvasPos);
-                break;
-            case Tool.Lasso:
-                ActiveDocument!.StartUpdateLassoSelection((VecI)canvasPos, selectionMode);
-                break;
-            case Tool.PathBasedPen:
-                ActiveDocument!.StartUpdatePathBasedPen(canvasPos);
-                break;
-            case Tool.LineBasedPen:
-                ActiveDocument!.StartUpdateLineBasedPen((VecI)canvasPos, new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A));
-                break;
-            case Tool.PixelPerfectPen:
-                ActiveDocument!.StartUpdatePixelPerfectPen((VecI)canvasPos, new SKColor(SelectedColor.R, SelectedColor.G, SelectedColor.B, SelectedColor.A));
-                break;
-            case Tool.Brightness:
-                ActiveDocument!.StartUpdateBrightness((VecI)canvasPos, (int)StrokeWidth, BrightnessCorrectionFactor, BrightnessRepeat, BrightnessDarken);
-                break;
-            case Tool.Eraser:
-                ActiveDocument!.StartUpdateLineBasedPen((VecI)canvasPos, SKColors.Transparent, true);
-                break;
-            case Tool.Pipette:
-                var color = ActiveDocument!.PickColor((VecI)canvasPos, ReferenceAllLayers);
-                SelectedColor = Color.FromArgb(color.Alpha, color.Red, color.Green, color.Blue);
-                break;
-        }
-    }
-
-    private void MouseUp(object? param)
-    {
-        if (!mouseIsDown)
-            return;
-        mouseIsDown = false;
-        ProcessToolMouseUp();
-        mouseHasMoved = false;
-    }
-
-    private void ProcessToolMouseUp()
-    {
-        if (mouseHasMoved)
-        {
-            switch (toolOnMouseDown)
-            {
-                case Tool.Rectangle:
-                    ActiveDocument!.EndRectangleDrawing();
-                    break;
-                case Tool.Ellipse:
-                    ActiveDocument!.EndEllipse();
-                    break;
-                case Tool.Line:
-                    ActiveDocument!.EndLine();
-                    break;
-                case Tool.SelectRectangle:
-                    ActiveDocument!.EndRectSelection();
-                    break;
-                case Tool.SelectEllipse:
-                    ActiveDocument!.EndEllipseSelection();
-                    break;
-                case Tool.ShiftLayer:
-                    ActiveDocument!.EndShiftLayer();
-                    break;
-                case Tool.Lasso:
-                    ActiveDocument!.EndLassoSelection();
-                    break;
-            }
-        }
-
-        switch (toolOnMouseDown)
-        {
-            case Tool.PathBasedPen:
-                ActiveDocument!.EndPathBasedPen();
-                break;
-            case Tool.PixelPerfectPen:
-                ActiveDocument!.EndUPixelPerfectPen();
-                break;
-            case Tool.Brightness:
-                ActiveDocument!.EndBrightness();
-                break;
-            case Tool.LineBasedPen:
-            case Tool.Eraser:
-                ActiveDocument!.EndLineBasedPen();
-                break;
-        }
-    }
-
-    private void LoadDocument(object? args)
-    {
-        OpenFileDialog dialog = new();
-        if (dialog.ShowDialog() != true)
-            return;
-        SerializableDocument serDocument;
-        try
-        {
-            serDocument = PixiParser.Deserialize(dialog.FileName);
-        }
-        catch (Exception)
-        {
-            return;
-        }
-
-        DocumentViewModel document = DocumentViewModel.FromSerializableDocument(this, serDocument, Path.GetFileName(dialog.FileName));
-        Documents.Add(document);
-    }
-
-    private void ChangeActiveTool(object? param)
-    {
-        if (param is null)
-            return;
-        activeTool = (Tool)param;
-    }
-}

+ 0 - 853
src/PixiEditorPrototype/Views/MainWindow.xaml

@@ -1,853 +0,0 @@
-<Window
-    x:Class="PixiEditorPrototype.Views.MainWindow"
-    x:ClassModifier="internal"
-    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-    xmlns:vm="clr-namespace:PixiEditorPrototype.ViewModels"
-    xmlns:conv="clr-namespace:PixiEditorPrototype.Converters"
-    xmlns:beh="clr-namespace:PixiEditorPrototype.Behaviors"
-    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
-    xmlns:pe="clr-namespace:PixiEditorPrototype"
-    xmlns:controls="clr-namespace:PixiEditorPrototype.CustomControls"
-    xmlns:to="clr-namespace:PixiEditorPrototype.CustomControls.TransformOverlay"
-    xmlns:vp="clr-namespace:PixiEditorPrototype.UserControls.Viewport"
-    xmlns:sk="clr-namespace:SkiaSharp;assembly=SkiaSharp"
-    xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
-    xmlns:models="clr-namespace:PixiEditorPrototype.Models"
-    xmlns:chen="clr-namespace:PixiEditor.ChangeableDocument.Enums;assembly=PixiEditor.ChangeableDocument"
-    xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
-    mc:Ignorable="d"
-    x:Name="window"
-    Title="MainWindow"
-    Height="800"
-    Width="1500">
-    <Window.DataContext>
-        <vm:ViewModelMain />
-    </Window.DataContext>
-    <Window.Resources>
-        <conv:BoolToVisibilityConverter
-            x:Key="BoolToVisibilityConverter" />
-        <conv:BlendModeToStringConverter
-            x:Key="BlendModeToStringConverter" />
-    </Window.Resources>
-    <DockPanel
-        Background="Gray">
-        <Border
-            BorderThickness="1"
-            Background="White"
-            BorderBrush="Black"
-            Width="280"
-            DockPanel.Dock="Right"
-            Margin="5">
-            <DockPanel>
-                <StackPanel
-                    DockPanel.Dock="Top"
-                    Orientation="Horizontal"
-                    Margin="0,5,0,0">
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.CreateNewLayerCommand}"
-                        Width="80">
-                        New Layer
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.CreateNewFolderCommand}"
-                        Width="80">
-                        New Folder
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.DeleteStructureMemberCommand}"
-                        Width="80">
-                        Delete
-                    </Button>
-                </StackPanel>
-                <StackPanel 
-                    DockPanel.Dock="Top" 
-                    Orientation="Horizontal" 
-                    Margin="0,5,0,0">
-                    <Button 
-                        Margin="5,0" 
-                        Command="{Binding ActiveDocument.CreateReferenceLayerCommand}" 
-                        Width="80">
-                        Add reference
-                    </Button>
-                </StackPanel>
-                <StackPanel
-                    DockPanel.Dock="Top"
-                    Orientation="Horizontal"
-                    Margin="0,5,0,0">
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.CreateMaskCommand}"
-                        Width="80">
-                        Create Mask
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.DeleteMaskCommand}"
-                        Width="80">
-                        Delete Mask
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.ApplyMaskCommand}"
-                        Width="80">
-                        Apply Mask
-                    </Button>
-                </StackPanel>
-                <StackPanel
-                    DockPanel.Dock="Top"
-                    Orientation="Horizontal"
-                    Margin="0,5,0,0">
-                    <controls:BlendModeComboBox
-                        Margin="5,0"
-                        Width="80"
-                        SelectedBlendMode="{Binding ActiveDocument.SelectedStructureMember.BlendModeBindable, Mode=TwoWay}" />
-                </StackPanel>
-                <StackPanel
-                    DockPanel.Dock="Top"
-                    Orientation="Horizontal"
-                    Margin="0,5,0,0">
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.ToggleLockTransparencyCommand}"
-                        Width="80">
-                        Lock Alpha
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        IsEnabled="False"
-                        Width="80">
-                        Lock
-                    </Button>
-                    <Button
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.ClipToMemberBelowCommand}"
-                        Width="80">
-                        Clip to below
-                    </Button>
-                </StackPanel>
-                <DockPanel
-                    DockPanel.Dock="Top"
-                    HorizontalAlignment="Stretch"
-                    Margin="0,5,0,5">
-                    <Button
-                        Width="80"
-                        Margin="5,0"
-                        Command="{Binding ActiveDocument.CombineCommand}">
-                        Merge
-                    </Button>
-                    <TextBlock
-                        Text="{Binding ActiveDocument.SelectedStructureMember.OpacityBindable, StringFormat=N2}"
-                        Margin="5,0"
-                        DockPanel.Dock="Right"
-                        VerticalAlignment="Center"
-                        TextAlignment="Center"
-                        d:Text="1.00"
-                        Width="30" />
-                    <Slider
-                        Minimum="0"
-                        Maximum="1"
-                        SmallChange="0.01"
-                        LargeChange="0.1"
-                        IsSnapToTickEnabled="True"
-                        TickFrequency="0.01"
-                        x:Name="opacitySlider"
-                        VerticalAlignment="Center"
-                        HorizontalAlignment="Stretch"
-                        Value="{Binding ActiveDocument.SelectedStructureMember.OpacityBindable, Mode=OneWay}">
-                        <i:Interaction.Behaviors>
-                            <beh:SliderUpdateBehavior
-                                DragValueChanged="{Binding ActiveDocument.SelectedStructureMember.UpdateOpacityCommand}"
-                                DragEnded="{Binding ActiveDocument.SelectedStructureMember.EndOpacityUpdateCommand}"
-                                ValueFromSlider="{Binding ElementName=opacitySlider, Path=Value}" />
-                        </i:Interaction.Behaviors>
-                    </Slider>
-                </DockPanel>
-                <TreeView
-                    ItemsSource="{Binding ActiveDocument.StructureRoot.Children}">
-                    <TreeView.ItemsPanel>
-                        <ItemsPanelTemplate>
-                            <pe:ReversedOrderStackPanel />
-                        </ItemsPanelTemplate>
-                    </TreeView.ItemsPanel>
-                    <TreeView.ItemContainerStyle>
-                        <Style
-                            TargetType="TreeViewItem">
-                            <Setter
-                                Property="ItemsPanel">
-                                <Setter.Value>
-                                    <ItemsPanelTemplate>
-                                        <pe:ReversedOrderStackPanel />
-                                    </ItemsPanelTemplate>
-                                </Setter.Value>
-                            </Setter>
-                        </Style>
-                    </TreeView.ItemContainerStyle>
-                    <TreeView.Resources>
-                        <HierarchicalDataTemplate
-                            DataType="{x:Type vm:FolderViewModel}"
-                            ItemsSource="{Binding Children}">
-                            <StackPanel
-                                Orientation="Horizontal"
-                                MinWidth="200"
-                                Background="Wheat">
-                                <StackPanel
-                                    Orientation="Vertical"
-                                    VerticalAlignment="Center">
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        HorizontalAlignment="Center"
-                                        IsChecked="{Binding IsVisibleBindable}" />
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        HorizontalAlignment="Center"
-                                        IsChecked="{Binding MaskIsVisibleBindable}"
-                                        Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                        Background="LightBlue" />
-                                </StackPanel>
-                                <Rectangle
-                                    Fill="DarkRed"
-                                    Width="8"
-                                    Margin="3,0"
-                                    Visibility="{Binding ClipToMemberBelowEnabledBindable, Converter={StaticResource BoolToVisibilityConverter}}" />
-                                <StackPanel>
-                                    <Button
-                                        Width="12"
-                                        Command="{Binding MoveUpCommand}">
-                                        ^
-                                    </Button>
-                                    <Button
-                                        Width="12"
-                                        Command="{Binding MoveDownCommand}">
-                                        v
-                                    </Button>
-                                </StackPanel>
-                                <Border
-                                    BorderBrush="Black"
-                                    BorderThickness="1"
-                                    MaxWidth="30"
-                                    MaxHeight="30"
-                                    HorizontalAlignment="Center"
-                                    VerticalAlignment="Center"
-                                    Margin="3,0,0,0">
-                                    <Image
-                                        Source="{Binding PreviewBitmap}">
-                                    </Image>
-                                </Border>
-                                <Border
-                                    Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                    BorderBrush="Black"
-                                    BorderThickness="1"
-                                    Background="White"
-                                    MaxWidth="30"
-                                    MaxHeight="30"
-                                    HorizontalAlignment="Center"
-                                    VerticalAlignment="Center"
-                                    Margin="3,0,0,0">
-                                    <Image
-                                        Source="{Binding MaskPreviewBitmap}">
-                                    </Image>
-                                </Border>
-                                <StackPanel
-                                    VerticalAlignment="Center">
-                                    <DockPanel
-                                        Margin="3, 0, 0, 0">
-                                        <TextBlock
-                                            Text="{Binding OpacityBindable}"
-                                            Width="25" />
-                                        <TextBlock
-                                            Text="{Binding BlendModeBindable, Converter={StaticResource BlendModeToStringConverter}}" />
-                                    </DockPanel>
-                                    <TextBox
-                                        HorizontalAlignment="Left"
-                                        Width="65"
-                                        Text="{Binding NameBindable}"
-                                        Margin="3, 0, 0, 0"
-                                        Height="20" />
-                                </StackPanel>
-                                <StackPanel
-                                    VerticalAlignment="Center"
-                                    Margin="3, 0, 0, 0">
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                        IsChecked="{Binding ShouldDrawOnMask}">
-                                        Edit Mask
-                                    </CheckBox>
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        IsChecked="{Binding IsSelected}">
-                                        Select
-                                    </CheckBox>
-                                </StackPanel>
-                                <StackPanel
-                                    Margin="3">
-                                    <TextBlock
-                                        Visibility="Collapsed">
-                                        🔒
-                                    </TextBlock>
-                                </StackPanel>
-                            </StackPanel>
-                        </HierarchicalDataTemplate>
-                        <DataTemplate
-                            DataType="{x:Type vm:LayerViewModel}">
-                            <StackPanel
-                                Orientation="Horizontal">
-                                <StackPanel
-                                    Orientation="Vertical"
-                                    VerticalAlignment="Center">
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        HorizontalAlignment="Center"
-                                        IsChecked="{Binding IsVisibleBindable}" />
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        HorizontalAlignment="Center"
-                                        IsChecked="{Binding MaskIsVisibleBindable}"
-                                        Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                        Background="LightBlue" />
-                                </StackPanel>
-                                <Rectangle
-                                    Fill="DarkRed"
-                                    Width="8"
-                                    Margin="3,0"
-                                    Visibility="{Binding ClipToMemberBelowEnabledBindable, Converter={StaticResource BoolToVisibilityConverter}}" />
-                                <StackPanel>
-                                    <Button
-                                        Width="12"
-                                        Command="{Binding MoveUpCommand}">
-                                        ^
-                                    </Button>
-                                    <Button
-                                        Width="12"
-                                        Command="{Binding MoveDownCommand}">
-                                        v
-                                    </Button>
-                                </StackPanel>
-                                <Border
-                                    BorderBrush="Black"
-                                    BorderThickness="1"
-                                    Background="White"
-                                    MaxWidth="30"
-                                    MaxHeight="30"
-                                    HorizontalAlignment="Center"
-                                    VerticalAlignment="Center"
-                                    Margin="3,0,0,0">
-                                    <Image
-                                        Source="{Binding PreviewBitmap}">
-                                    </Image>
-                                </Border>
-                                <Border
-                                    Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                    BorderBrush="Black"
-                                    BorderThickness="1"
-                                    Background="White"
-                                    MaxWidth="30"
-                                    MaxHeight="30"
-                                    HorizontalAlignment="Center"
-                                    VerticalAlignment="Center"
-                                    Margin="3,0,0,0">
-                                    <Image
-                                        Source="{Binding MaskPreviewBitmap}">
-                                    </Image>
-                                </Border>
-                                <StackPanel
-                                    VerticalAlignment="Center">
-                                    <DockPanel
-                                        Margin="3, 0, 0, 0">
-                                        <TextBlock
-                                            Text="{Binding OpacityBindable}"
-                                            Width="25" />
-                                        <TextBlock
-                                            Text="{Binding BlendModeBindable, Converter={StaticResource BlendModeToStringConverter}}" />
-                                    </DockPanel>
-                                    <TextBox
-                                        HorizontalAlignment="Left"
-                                        Width="65"
-                                        Text="{Binding NameBindable}"
-                                        Margin="3, 0, 0, 0"
-                                        Height="20" />
-                                </StackPanel>
-                                <StackPanel
-                                    VerticalAlignment="Center"
-                                    Margin="3, 0, 0, 0">
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
-                                        IsChecked="{Binding ShouldDrawOnMask}">
-                                        Edit Mask
-                                    </CheckBox>
-                                    <CheckBox
-                                        VerticalAlignment="Center"
-                                        IsChecked="{Binding IsSelected}">
-                                        Select
-                                    </CheckBox>
-                                </StackPanel>
-                                <StackPanel
-                                    Margin="3">
-                                    <TextBlock
-                                        Visibility="{Binding LockTransparencyBindable, Converter={StaticResource BoolToVisibilityConverter}}">
-                                        🙾
-                                    </TextBlock>
-                                    <TextBlock
-                                        Visibility="Collapsed">
-                                        🔒
-                                    </TextBlock>
-                                </StackPanel>
-                            </StackPanel>
-                        </DataTemplate>
-                    </TreeView.Resources>
-                </TreeView>
-            </DockPanel>
-        </Border>
-        <Border
-            BorderThickness="1"
-            Background="White"
-            BorderBrush="Black"
-            DockPanel.Dock="Top"
-            Margin="5">
-            <WrapPanel
-                Orientation="Horizontal"
-                Background="White">
-                <Button
-                    Width="50"
-                    Margin="5"
-                    Command="{Binding LoadDocumentCommand}">
-                    Open
-                </Button>
-                <Button
-                    Width="50"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.UndoCommand}">
-                    Undo
-                </Button>
-                <Button
-                    Width="50"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.RedoCommand}">
-                    Redo
-                </Button>
-                <Button
-                    Width="100"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.ClearSelectionCommand}">
-                    Clear selection
-                </Button>
-                <Button
-                    Width="110"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.TransformSelectionPathCommand}">
-                    Transform sel. path
-                </Button>
-                <Button
-                    Width="110"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.TransformSelectedAreaCommand}">
-                    Transform sel. area
-                </Button>
-                <ComboBox
-                    Width="70"
-                    Height="20"
-                    Margin="5"
-                    SelectedIndex="0"
-                    x:Name="selectionModeComboBox">
-                    <ComboBoxItem
-                        Tag="{x:Static chen:SelectionMode.New}">
-                        New
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:SelectionMode.Add}">
-                        Add
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:SelectionMode.Subtract}">
-                        Subtract
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:SelectionMode.Intersect}">
-                        Intersect
-                    </ComboBoxItem>
-                    <i:Interaction.Triggers>
-                        <i:EventTrigger
-                            EventName="SelectionChanged">
-                            <i:InvokeCommandAction
-                                Command="{Binding SetSelectionModeCommand}"
-                                CommandParameter="{Binding SelectedItem.Tag, ElementName=selectionModeComboBox}" />
-                        </i:EventTrigger>
-                    </i:Interaction.Triggers>
-                </ComboBox>
-                <ComboBox
-                    Width="70"
-                    Height="20"
-                    Margin="5"
-                    SelectedIndex="0"
-                    x:Name="lineCapComboBox">
-                    <ComboBoxItem
-                        Tag="{x:Static sk:SKStrokeCap.Butt}">
-                        Butt
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static sk:SKStrokeCap.Round}">
-                        Round
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static sk:SKStrokeCap.Square}">
-                        Square
-                    </ComboBoxItem>
-                    <i:Interaction.Triggers>
-                        <i:EventTrigger
-                            EventName="SelectionChanged">
-                            <i:InvokeCommandAction
-                                Command="{Binding SetLineCapCommand}"
-                                CommandParameter="{Binding SelectedItem.Tag, ElementName=lineCapComboBox}" />
-                        </i:EventTrigger>
-                    </i:Interaction.Triggers>
-                </ComboBox>
-                <ComboBox
-                    Width="70"
-                    Height="20"
-                    Margin="5"
-                    SelectedIndex="0"
-                    x:Name="resizeAnchorComboBox">
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.TopLeft}">
-                        Top Left
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.Top}">
-                        Top
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.TopRight}">
-                        Top Right
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.Left}">
-                        Left
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.Center}">
-                        Center
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.Right}">
-                        Right
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.BottomLeft}">
-                        Bottom Left
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.Bottom}">
-                        Bottom
-                    </ComboBoxItem>
-                    <ComboBoxItem
-                        Tag="{x:Static chen:ResizeAnchor.BottomRight}">
-                        Bottom Right
-                    </ComboBoxItem>
-                    <i:Interaction.Triggers>
-                        <i:EventTrigger
-                            EventName="SelectionChanged">
-                            <i:InvokeCommandAction
-                                Command="{Binding SetResizeAnchorCommand}"
-                                CommandParameter="{Binding SelectedItem.Tag, ElementName=resizeAnchorComboBox}" />
-                        </i:EventTrigger>
-                    </i:Interaction.Triggers>
-                </ComboBox>
-                <Button
-                    Width="120"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.ClearHistoryCommand}">
-                    Clear undo history
-                </Button>
-                <Button
-                    Width="100"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.PasteImageCommand}">
-                    Paste Image
-                </Button>
-                <Button
-                    Width="100"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.ApplyTransformCommand}">
-                    Apply Transform
-                </Button>
-                <Label>Pen size:</Label>
-                <TextBox
-                    Width="30"
-                    Margin="5"
-                    Text="{Binding StrokeWidth}" />
-                <TextBox
-                    Width="30"
-                    Margin="5"
-                    Text="{Binding ActiveDocument.ResizeWidth}" />
-                <TextBox
-                    Width="30"
-                    Margin="5"
-                    Text="{Binding ActiveDocument.ResizeHeight}" />
-                <Button
-                    Width="80"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.ResizeCanvasCommand}">
-                    Resize Canvas
-                </Button>
-                <Button
-                    Width="80"
-                    Margin="5"
-                    Command="{Binding ActiveDocument.ResizeImageCommand}">
-                    Resize Image
-                </Button>
-                <CheckBox
-                    IsChecked="{Binding BrightnessRepeat}">
-                    Brgt Repeat
-                </CheckBox>
-                <CheckBox
-                    IsChecked="{Binding BrightnessDarken}">
-                    Brgt Darken
-                </CheckBox>
-                <Label>BrgtCorFact</Label>
-                <TextBox Text="{Binding BrightnessCorrectionFactor}"/>
-            </WrapPanel>
-        </Border>
-        <Border
-            BorderThickness="1"
-            Background="White"
-            BorderBrush="Black"
-            DockPanel.Dock="Left"
-            Margin="5">
-            <StackPanel
-                Orientation="Vertical"
-                Background="White">
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Rectangle}">
-                    Rect
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Line}">
-                    Line
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Ellipse}">
-                    Ellipse
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.PathBasedPen}">
-                    Path Pen
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.LineBasedPen}">
-                    Line Pen
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.PixelPerfectPen}">
-                    P Perf Pen
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Eraser}">
-                    Eraser
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Brightness}">
-                    Brightness
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.SelectRectangle}">
-                    Select
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.SelectEllipse}">
-                    Select Ellipse
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Lasso}">
-                    Lasso
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.ShiftLayer}">
-                    Shift Layer
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.FloodFill}">
-                    Fill
-                </Button>
-                <Button
-                    Width="70"
-                    Margin="5"
-                    Command="{Binding ChangeActiveToolCommand}"
-                    CommandParameter="{x:Static models:Tool.Pipette}">
-                    Pipette
-                </Button>
-                <colorpicker:PortableColorPicker
-                    Margin="5"
-                    SelectedColor="{Binding SelectedColor, Mode=TwoWay}"
-                    Width="30"
-                    Height="30" />
-                <RadioButton
-                    GroupName="zoomboxMode"
-                    Margin="5,0"
-                    IsChecked="{Binding NormalZoombox, Mode=OneWayToSource}">
-                    Normal
-                </RadioButton>
-                <RadioButton
-                    GroupName="zoomboxMode"
-                    Margin="5,0"
-                    IsChecked="{Binding MoveZoombox, Mode=OneWayToSource}">
-                    Move
-                </RadioButton>
-                <RadioButton
-                    GroupName="zoomboxMode"
-                    Margin="5,0"
-                    IsChecked="{Binding RotateZoombox, Mode=OneWayToSource}">
-                    Rotate
-                </RadioButton>
-                <CheckBox
-                    x:Name="flipXCheckbox"
-                    Margin="5, 0">
-                    Flip X
-                </CheckBox>
-                <CheckBox
-                    x:Name="flipYCheckbox"
-                    Margin="5, 0">
-                    Flip Y
-                </CheckBox>
-                <CheckBox
-                    x:Name="keepOriginalImageCheckbox"
-                    Margin="5, 0"
-                    IsChecked="{Binding KeepOriginalImageOnTransform}">
-                    Keep area
-                </CheckBox>
-                <CheckBox
-                    Margin="5, 0"
-                    IsChecked="{Binding ReferenceAllLayers}">
-                    Ref all layers
-                </CheckBox>
-                <CheckBox
-                    x:Name="horizontalSymmetryCheckbox"
-                    Margin="5,0"
-                    IsChecked="{Binding ActiveDocument.HorizontalSymmetryAxisEnabledBindable}">
-                    Hor Sym
-                </CheckBox>
-                <CheckBox
-                    x:Name="verticalSymmetryCheckbox"
-                    Margin="5,0"
-                    IsChecked="{Binding ActiveDocument.VerticalSymmetryAxisEnabledBindable}">
-                    Ver Sym
-                </CheckBox>
-            </StackPanel>
-        </Border>
-        <TabControl
-            ItemsSource="{Binding Documents}"
-            SelectedIndex="{Binding ActiveDocumentIndex}">
-            <TabControl.ItemTemplate>
-                <DataTemplate>
-                    <StackPanel
-                        Orientation="Horizontal">
-                        <Image
-                            Source="{Binding PreviewBitmap}"
-                            Width="30"
-                            Height="30" />
-                        <TextBlock
-                            Text="{Binding Name}" />
-                    </StackPanel>
-                </DataTemplate>
-            </TabControl.ItemTemplate>
-            <TabControl.ContentTemplate>
-                <DataTemplate>
-                    <Grid
-                        Background="Gray">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition />
-                            <ColumnDefinition />
-                        </Grid.ColumnDefinitions>
-                        <Border
-                            BorderThickness="1"
-                            BorderBrush="Black"
-                            Margin="5">
-                            <vp:Viewport
-                                Delayed="True"
-                                Document="{Binding}"
-                                FlipX="{Binding ElementName=flipXCheckbox, Path=IsChecked}"
-                                FlipY="{Binding ElementName=flipYCheckbox, Path=IsChecked}"
-                                ZoomMode="{Binding ElementName=window, Path=DataContext.ZoomboxMode, Mode=TwoWay}"
-                                MouseDownCommand="{Binding ElementName=window, Path=DataContext.MouseDownCommand}"
-                                MouseMoveCommand="{Binding ElementName=window, Path=DataContext.MouseMoveCommand}"
-                                MouseUpCommand="{Binding ElementName=window, Path=DataContext.MouseUpCommand}"
-                                Tag="First" />
-                        </Border>
-                        <Border
-                            BorderThickness="1"
-                            BorderBrush="Black"
-                            Margin="5"
-                            Grid.Column="1">
-                            <vp:Viewport
-                                Document="{Binding}"
-                                FlipX="{Binding ElementName=flipXCheckbox, Path=IsChecked}"
-                                FlipY="{Binding ElementName=flipYCheckbox, Path=IsChecked}"
-                                ZoomMode="{Binding ElementName=window, Path=DataContext.ZoomboxMode, Mode=TwoWay}"
-                                MouseDownCommand="{Binding ElementName=window, Path=DataContext.MouseDownCommand}"
-                                MouseMoveCommand="{Binding ElementName=window, Path=DataContext.MouseMoveCommand}"
-                                MouseUpCommand="{Binding ElementName=window, Path=DataContext.MouseUpCommand}"
-                                Tag="Second" />
-                        </Border>
-                        <Grid
-                            Grid.ColumnSpan="2"
-                            Background="#BB000000"
-                            Visibility="{Binding Busy, Converter={StaticResource BoolToVisibilityConverter}}">
-                            <TextBlock
-                                Foreground="White"
-                                HorizontalAlignment="Center"
-                                VerticalAlignment="Center"
-                                FontSize="20">
-                                Loading...
-                            </TextBlock>
-                        </Grid>
-                    </Grid>
-                </DataTemplate>
-            </TabControl.ContentTemplate>
-        </TabControl>
-    </DockPanel>
-</Window>

+ 0 - 11
src/PixiEditorPrototype/Views/MainWindow.xaml.cs

@@ -1,11 +0,0 @@
-using System.Windows;
-
-namespace PixiEditorPrototype.Views;
-
-internal partial class MainWindow : Window
-{
-    public MainWindow()
-    {
-        InitializeComponent();
-    }
-}