Explorar el Código

Refactoring tool system and writing tests

flabbet hace 5 años
padre
commit
2c18a3dc54

+ 48 - 0
PixiEditorDotNetCore3/Models/CoordinatesCalculator.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditorDotNetCore3.Models
+{
+    public static class CoordinatesCalculator
+    {
+        /// <summary>
+        /// Calculates center of thickness * thickness rectangle
+        /// </summary>
+        /// <param name="startPosition">Top left position of rectangle</param>
+        /// <param name="thickness">Thickness of rectangle</param>
+        /// <returns></returns>
+        public static DoubleCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
+        {
+            int x1, x2, y1, y2;
+            if (thickness % 2 == 0)
+            {
+                x2 = startPosition.X + thickness / 2;
+                y2 = startPosition.Y + thickness / 2;
+                x1 = x2 - thickness;
+                y1 = y2 - thickness;
+            }
+            else
+            {
+                x2 = startPosition.X + (((thickness - 1) / 2) + 1);
+                y2 = startPosition.Y + (((thickness - 1) / 2) + 1);
+                x1 = x2 - thickness;
+                y1 = y2 - thickness;
+            }
+            return new DoubleCords(new Coordinates(x1, y1), new Coordinates(x2, y2));
+        }
+
+        public static Coordinates[] RectangleToCoordinates(int x1, int y1, int x2, int y2)
+        {
+            List<Coordinates> coordinates = new List<Coordinates>();
+            for (int y = y1; y < y1 + (y2 - y1); y++)
+            {
+                for (int x = x1; x < x1 + (x2 - x1); x++)
+                {
+                    coordinates.Add(new Coordinates(x, y));
+                }
+            }
+            return coordinates.ToArray();
+        }
+    }
+}

+ 2 - 2
PixiEditorDotNetCore3/Models/DCords.cs → PixiEditorDotNetCore3/Models/DoubleCords.cs

@@ -6,12 +6,12 @@ using System.Threading.Tasks;
 
 namespace PixiEditorDotNetCore3.Models
 {
-    public class DCords
+    public class DoubleCords
     {
         public Coordinates Coords1 { get; set; }
         public Coordinates Coords2 { get; set; }
         
-        public DCords(Coordinates cords1, Coordinates cords2)
+        public DoubleCords(Coordinates cords1, Coordinates cords2)
         {
             Coords1 = cords1;
             Coords2 = cords2;

+ 9 - 0
PixiEditorDotNetCore3/Models/Layer.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Helpers;
+using PixiEditorDotNetCore3.Models.Tools;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -41,5 +42,13 @@ namespace PixiEditorDotNetCore3.Models
             Width = (int) layerBitmap.Width;
             Height = (int) layerBitmap.Height;
         }
+
+        public void ApplyPixels(BitmapPixelChanges pixels, Color color)
+        {
+            foreach (var coords in pixels.ChangedCoordinates)
+            {
+                LayerBitmap.SetPixel(coords.X, coords.Y, color);
+            }
+        }
     }
 }

+ 1 - 1
PixiEditorDotNetCore3/Models/StackEx.cs

@@ -44,7 +44,7 @@ namespace PixiEditorDotNetCore3.Models
                 return temp;
             }
             else
-                return default(T);
+                return default;
         }
 
         public void PushToBottom(T item)

+ 19 - 0
PixiEditorDotNetCore3/Models/Tools/BitmapPixelChanges.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+     public struct BitmapPixelChanges
+    {
+        public Coordinates[] ChangedCoordinates { get; set; }
+        public Color PixelsColor { get; set; }
+
+        public BitmapPixelChanges(Coordinates[] changedCoordinates, Color color)
+        {
+            ChangedCoordinates = changedCoordinates;
+            PixelsColor = color;
+        }
+    }
+}

+ 13 - 0
PixiEditorDotNetCore3/Models/Tools/Tool.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools
+{
+    public abstract class Tool
+    {
+        public abstract BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize);
+        public abstract ToolType GetToolType();
+    }
+}

+ 19 - 93
PixiEditorDotNetCore3/Models/Tools/ToolSet.cs

@@ -15,10 +15,16 @@ namespace PixiEditorDotNetCore3.Models.Tools
 {
     public class ToolSet
     {
+        public List<Tool> Tools { get; set; } = new List<Tool>();
         private Coordinates _activeCoordinates = new Coordinates();
         private bool _toolIsExecuting = false;
         private int _asyncDelay = 15;
-        private WriteableBitmap _oldBitmap;
+
+
+        public ToolSet(List<Tool> tools)
+        {
+            Tools = tools;
+        }
 
         /// <summary>
         /// Executes tool action
@@ -29,60 +35,23 @@ namespace PixiEditorDotNetCore3.Models.Tools
         /// <param name="toolSize">Size/thickness of tool</param>
         /// <param name="tool">Tool to execute</param>
         /// <returns></returns>
-        public Layer ExecuteTool(Layer layer, Coordinates startingCoords, Color color,int toolSize, ToolType tool)
+        public void ExecuteTool(Layer layer, Coordinates startingCoords, Color color,int toolSize, ToolType tool)
         {
-            if (toolSize < 1) return null;
-            Layer cLayer = layer;
+            if (toolSize < 1) return;
+            BitmapPixelChanges changes;
 
-            _oldBitmap = new WriteableBitmap(layer.LayerBitmap);
 
-            switch (tool)
-            {
-                case ToolType.Pen:
-                    cLayer.LayerBitmap = DrawPixel(cLayer.LayerBitmap, startingCoords, toolSize,color);
-                    break;
-                case ToolType.Bucket:
-                    cLayer.LayerBitmap = FloodFill(cLayer.LayerBitmap, startingCoords, color);
-                    break;
-                case ToolType.Line:
-                    if (_toolIsExecuting == false)
-                    {
-                        LineAsync(cLayer, startingCoords, color, toolSize);
-                    }
-                    break;
-                case ToolType.Circle:
-                    if(_toolIsExecuting == false)
-                    {
-                        CircleAsync(cLayer, startingCoords, color);
-                    }
-                    break;
-                case ToolType.Rectangle:
-                    if(_toolIsExecuting == false)
-                    {
-                        RectangleAsync(cLayer, startingCoords, color);
-                    }
-                    break;              
-                case ToolType.Earser:
-                    cLayer.LayerBitmap = DrawPixel(cLayer.LayerBitmap, startingCoords, toolSize, Colors.Transparent);
-                    break;
-                case ToolType.Lighten:
-                    if(Mouse.LeftButton == MouseButtonState.Pressed)
-                    {
-                        cLayer.LayerBitmap = Lighten(cLayer.LayerBitmap, startingCoords);
-                    }
-                    else if(Mouse.RightButton == MouseButtonState.Pressed)
-                    {
-                        cLayer.LayerBitmap = Darken(cLayer.LayerBitmap, startingCoords);
-                    }
-                    break;
-            }
+            Tool selectedTool = Tools.Find(x => x.GetToolType() == tool);
+            changes = selectedTool.Use(layer, startingCoords, color, toolSize);
+
             if (tool != ToolType.ColorPicker)
             {
-                UndoManager.RecordChanges("ActiveLightLayer", new LightLayer(_oldBitmap.ToByteArray(), (int)_oldBitmap.Height, (int)_oldBitmap.Width),
+                UndoManager.RecordChanges("ActiveLightLayer", new LightLayer(layer.LayerBitmap.ToByteArray(), (int)layer.LayerBitmap.Height, (int)layer.LayerBitmap.Width),
                     $"{tool.ToString()} Tool.");
             }
 
-            return cLayer;
+            layer.ApplyPixels(changes, color);
+
         }
 
         /// <summary>
@@ -96,7 +65,7 @@ namespace PixiEditorDotNetCore3.Models.Tools
         {
             bitmap.Clear();
             bitmap.Blit(new Rect(new Size(bitmap.Width, bitmap.Height)), bitmap, new Rect(new Size(bitmap.Width, bitmap.Height)), WriteableBitmapExtensions.BlendMode.Additive);
-            DCords centerCords = CalculateThicknessCenter(pixelCoordinates, highlightThickness);
+            DoubleCords centerCords = CoordinatesCalculator.CalculateThicknessCenter(pixelCoordinates, highlightThickness);
             bitmap.FillRectangle(centerCords.Coords1.X, centerCords.Coords1.Y, centerCords.Coords2.X, centerCords.Coords2.Y, color);
         }
 
@@ -106,52 +75,9 @@ namespace PixiEditorDotNetCore3.Models.Tools
         /// <param name="cords">Current coordinates</param>
         public void UpdateCoordinates(Coordinates cords)
         {
-                _activeCoordinates = cords;
-        }
-
-        /// <summary>
-        /// Fills pixel(s) with choosen color
-        /// </summary>
-        /// <param name="canvas">Bitmap to operate on.</param>
-        /// <param name="pixelPosition">Coordinates of pixel.</param>
-        /// <param name="color">Color to be set.</param>
-        private WriteableBitmap DrawPixel(WriteableBitmap canvas, Coordinates pixelPosition,int thickness,Color color)
-        {
-            WriteableBitmap bm = canvas;
-            int x1, y1, x2, y2;
-            DCords centeredCoords = CalculateThicknessCenter(pixelPosition, thickness);
-            x1 = centeredCoords.Coords1.X;
-            y1 = centeredCoords.Coords1.Y;
-            x2 = centeredCoords.Coords2.X;
-            y2 = centeredCoords.Coords2.Y;
-            bm.FillRectangle(x1, y1, x2, y2, color);
-            return bm;
-        }
-        /// <summary>
-        /// Calculates center of thickness * thickness rectangle
-        /// </summary>
-        /// <param name="startPosition">Top left position of rectangle</param>
-        /// <param name="thickness">Thickness of rectangle</param>
-        /// <returns></returns>
-        private static DCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
-        {
-            int x1, x2, y1, y2;
-            if (thickness % 2 == 0)
-            {
-                x2 = startPosition.X + thickness / 2;
-                y2 = startPosition.Y + thickness / 2;
-                x1 = x2 - thickness;
-                y1 = y2 - thickness;
-            }
-            else
-            {
-                x2 = startPosition.X + (((thickness - 1) / 2) + 1);
-                y2 = startPosition.Y + (((thickness - 1) / 2) + 1);
-                x1 = x2 - thickness;
-                y1 = y2 - thickness;
-            }
-            return new DCords(new Coordinates(x1, y1), new Coordinates(x2, y2));
+            _activeCoordinates = cords;
         }
+        
 
         /// <summary>
         /// Fills area with color (forest fire alghoritm)

+ 31 - 0
PixiEditorDotNetCore3/Models/Tools/Tools/Pen.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Media;
+
+namespace PixiEditorDotNetCore3.Models.Tools.Tools
+{
+    public class Pen : Tool
+    {
+        public override BitmapPixelChanges Use(Layer layer, Coordinates startingCoords, Color color, int toolSize)
+        {
+            return Draw(startingCoords, color, toolSize);
+        }
+
+        public BitmapPixelChanges Draw(Coordinates startingCoords, Color color, int toolSize)
+        {
+            int x1, y1, x2, y2;
+            DoubleCords centeredCoords = CoordinatesCalculator.CalculateThicknessCenter(startingCoords, toolSize);
+            x1 = centeredCoords.Coords1.X;
+            y1 = centeredCoords.Coords1.Y;
+            x2 = centeredCoords.Coords2.X;
+            y2 = centeredCoords.Coords2.Y;
+            return new BitmapPixelChanges(CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2), color);
+        }
+
+        public override ToolType GetToolType()
+        {
+            return ToolType.Pen;
+        }
+    }
+}

+ 1 - 1
PixiEditorDotNetCore3/ViewModels/ViewModelMain.cs

@@ -155,7 +155,7 @@ namespace PixiEditor.ViewModels
             MouseUpCommand = new RelayCommand(MouseUp);
             RecenterZoomboxCommand = new RelayCommand(RecenterZoombox);
             OpenFileCommand = new RelayCommand(OpenFile);
-            primaryToolSet = new ToolSet();
+            primaryToolSet = new ToolSet(new List<Tool> { new PixiEditorDotNetCore3.Models.Tools.Tools.Pen() });
             UndoManager.SetMainRoot(this);
         }
 

+ 8 - 10
PixiEditorTests/PixiEditorTests.csproj

@@ -7,13 +7,13 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="nunit" Version="3.11.0" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="ToolsTests\" />
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.5">
+      <PrivateAssets>all</PrivateAssets>
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+    </PackageReference>
+    <PackageReference Include="nunit" Version="3.12.0" />
+    <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
   </ItemGroup>
 
   <ItemGroup>
@@ -21,9 +21,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Reference Include="PresentationCore">
-      <HintPath>C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll</HintPath>
-    </Reference>
+    <Folder Include="ViewModelsTests\" />
   </ItemGroup>
 
 </Project>

+ 25 - 0
PixiEditorTests/WorkspaceTests/ImageGeneratorTests.cs

@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using PixiEditorDotNetCore3.Models;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace PixiEditorTests.ToolsTests
+{
+    [TestFixture, Apartment(System.Threading.ApartmentState.STA)]
+    public class ImageGeneratorTests
+    {
+        [TestCase(16,16)]
+        [TestCase(1024,12)]
+        [TestCase(50000,50000)]
+        public void ImageIsPixelArtReady(int width, int height)
+        {
+            Image img = ImageGenerator.GenerateForPixelArts(width, height);
+            
+            Assert.IsTrue(img.Stretch == Stretch.Uniform && RenderOptions.GetBitmapScalingMode(img) == BitmapScalingMode.NearestNeighbor
+                && RenderOptions.GetEdgeMode(img) == EdgeMode.Aliased && img.Width == width && img.Height == height);
+        }
+    }
+}

+ 20 - 0
PixiEditorTests/WorkspaceTests/ToolsTests/CoordinatesCalculatorTests.cs

@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using PixiEditorDotNetCore3.Models;
+using PixiEditorDotNetCore3.Models.Tools.Tools;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace PixiEditorTests.WorkspaceTests.ToolsTests
+{
+    [TestFixture]
+    public class CoordinatesCalculatorTests
+    {
+        [TestCase(0,0,2,2, ExpectedResult = 9)]
+        [TestCase(0,0,10,10, ExpectedResult = 121)]
+        public int RectangleToCoordinatesAmountTest(int x1, int y1, int x2, int y2)
+        {
+            return CoordinatesCalculator.RectangleToCoordinates(x1, y1, x2, y2).Length;
+        }
+    }
+}