Browse Source

Fix ellipse drawing algorithm and brightness tool

Equbuxu 3 years ago
parent
commit
748ae49c84

+ 4 - 4
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -112,7 +112,7 @@ namespace PixiEditor.Models.DataHolders
         /// </summary>
         /// </summary>
         public void ClipCanvas()
         public void ClipCanvas()
         {
         {
-            DoubleCords points = GetEdgePoints(Layers);
+            DoubleCoords points = GetEdgePoints(Layers);
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
             int biggestX = points.Coords2.X;
             int biggestX = points.Coords2.X;
@@ -157,7 +157,7 @@ namespace PixiEditor.Models.DataHolders
                 return;
                 return;
             }
             }
 
 
-            DoubleCords points = GetEdgePoints(layersToCenter);
+            DoubleCoords points = GetEdgePoints(layersToCenter);
 
 
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
@@ -238,7 +238,7 @@ namespace PixiEditor.Models.DataHolders
             return 0;
             return 0;
         }
         }
 
 
-        private DoubleCords GetEdgePoints(IEnumerable<Layer> layers)
+        private DoubleCoords GetEdgePoints(IEnumerable<Layer> layers)
         {
         {
             if (Layers.Count == 0)
             if (Layers.Count == 0)
             {
             {
@@ -274,7 +274,7 @@ namespace PixiEditor.Models.DataHolders
                 }
                 }
             }
             }
 
 
-            return new DoubleCords(
+            return new DoubleCoords(
                 new Coordinates(smallestX, smallestY),
                 new Coordinates(smallestX, smallestY),
                 new Coordinates(biggestX, biggestY));
                 new Coordinates(biggestX, biggestY));
         }
         }

+ 13 - 13
PixiEditor/Models/ImageManipulation/ToolCalculator.cs

@@ -117,7 +117,7 @@ namespace PixiEditor.Models.ImageManipulation
             List<Coordinates> output)
             List<Coordinates> output)
         {
         {
 
 
-            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
+            DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
 
 
             CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, output);
             CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, output);
 
 
@@ -131,13 +131,13 @@ namespace PixiEditor.Models.ImageManipulation
             Coordinates start,
             Coordinates start,
             Coordinates end, bool fill, int thickness, List<Coordinates> output)
             Coordinates end, bool fill, int thickness, List<Coordinates> output)
         {
         {
-            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
+            DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
             CalculateRectanglePoints(fixedCoordinates, output);
             CalculateRectanglePoints(fixedCoordinates, output);
 
 
             for (int i = 1; i < (int)Math.Floor(thickness / 2f) + 1; i++)
             for (int i = 1; i < (int)Math.Floor(thickness / 2f) + 1; i++)
             {
             {
                 CalculateRectanglePoints(
                 CalculateRectanglePoints(
-                    new DoubleCords(
+                    new DoubleCoords(
                     new Coordinates(fixedCoordinates.Coords1.X - i, fixedCoordinates.Coords1.Y - i),
                     new Coordinates(fixedCoordinates.Coords1.X - i, fixedCoordinates.Coords1.Y - i),
                     new Coordinates(fixedCoordinates.Coords2.X + i, fixedCoordinates.Coords2.Y + i)), output);
                     new Coordinates(fixedCoordinates.Coords2.X + i, fixedCoordinates.Coords2.Y + i)), output);
             }
             }
@@ -145,7 +145,7 @@ namespace PixiEditor.Models.ImageManipulation
             for (int i = 1; i < (int)Math.Ceiling(thickness / 2f); i++)
             for (int i = 1; i < (int)Math.Ceiling(thickness / 2f); i++)
             {
             {
                 CalculateRectanglePoints(
                 CalculateRectanglePoints(
-                    new DoubleCords(
+                    new DoubleCoords(
                     new Coordinates(fixedCoordinates.Coords1.X + i, fixedCoordinates.Coords1.Y + i),
                     new Coordinates(fixedCoordinates.Coords1.X + i, fixedCoordinates.Coords1.Y + i),
                     new Coordinates(fixedCoordinates.Coords2.X - i, fixedCoordinates.Coords2.Y - i)), output);
                     new Coordinates(fixedCoordinates.Coords2.X - i, fixedCoordinates.Coords2.Y - i)), output);
             }
             }
@@ -156,7 +156,7 @@ namespace PixiEditor.Models.ImageManipulation
             }
             }
         }
         }
 
 
-        public static DoubleCords CalculateCoordinatesForShapeRotation(
+        public static DoubleCoords CalculateCoordinatesForShapeRotation(
             Coordinates startingCords,
             Coordinates startingCords,
             Coordinates secondCoordinates)
             Coordinates secondCoordinates)
         {
         {
@@ -164,33 +164,33 @@ namespace PixiEditor.Models.ImageManipulation
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, startingCords.Y));
                     new Coordinates(startingCords.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
             }
             }
 
 
             if (startingCords.Y > currentCoordinates.Y)
             if (startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y));
                     new Coordinates(currentCoordinates.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y));
                     new Coordinates(startingCords.X, currentCoordinates.Y));
             }
             }
 
 
-            return new DoubleCords(startingCords, secondCoordinates);
+            return new DoubleCoords(startingCords, secondCoordinates);
         }
         }
 
 
         private static void CalculateFillForEllipse(List<Coordinates> outlineCoordinates)
         private static void CalculateFillForEllipse(List<Coordinates> outlineCoordinates)
@@ -308,9 +308,9 @@ namespace PixiEditor.Models.ImageManipulation
         private static void CalculateRectangleFillNonAlloc(Coordinates start, Coordinates end, int thickness, List<Coordinates> output)
         private static void CalculateRectangleFillNonAlloc(Coordinates start, Coordinates end, int thickness, List<Coordinates> output)
         {
         {
             int offset = (int)Math.Ceiling(thickness / 2f);
             int offset = (int)Math.Ceiling(thickness / 2f);
-            DoubleCords fixedCords = CalculateCoordinatesForShapeRotation(start, end);
+            DoubleCoords fixedCords = CalculateCoordinatesForShapeRotation(start, end);
 
 
-            DoubleCords innerCords = new DoubleCords
+            DoubleCoords innerCords = new DoubleCoords
             {
             {
                 Coords1 = new Coordinates(fixedCords.Coords1.X + offset, fixedCords.Coords1.Y + offset),
                 Coords1 = new Coordinates(fixedCords.Coords1.X + offset, fixedCords.Coords1.Y + offset),
                 Coords2 = new Coordinates(fixedCords.Coords2.X - (offset - 1), fixedCords.Coords2.Y - (offset - 1))
                 Coords2 = new Coordinates(fixedCords.Coords2.X - (offset - 1), fixedCords.Coords2.Y - (offset - 1))
@@ -335,7 +335,7 @@ namespace PixiEditor.Models.ImageManipulation
             }
             }
         }
         }
 
 
-        private static void CalculateRectanglePoints(DoubleCords coordinates, List<Coordinates> output)
+        private static void CalculateRectanglePoints(DoubleCoords coordinates, List<Coordinates> output)
         {
         {
             for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
             for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
             {
             {

+ 8 - 8
PixiEditor/Models/Layers/Layer.cs

@@ -407,8 +407,8 @@ namespace PixiEditor.Models.Layers
             }
             }
 
 
             ResetOffset(pixels);
             ResetOffset(pixels);
-            Tuple<DoubleCords, bool> borderData = ExtractBorderData(pixels);
-            DoubleCords minMaxCords = borderData.Item1;
+            Tuple<DoubleCoords, bool> borderData = ExtractBorderData(pixels);
+            DoubleCoords minMaxCords = borderData.Item1;
             int newMaxX = minMaxCords.Coords2.X - OffsetX;
             int newMaxX = minMaxCords.Coords2.X - OffsetX;
             int newMaxY = minMaxCords.Coords2.Y - OffsetY;
             int newMaxY = minMaxCords.Coords2.Y - OffsetY;
             int newMinX = minMaxCords.Coords1.X - OffsetX;
             int newMinX = minMaxCords.Coords1.X - OffsetX;
@@ -463,7 +463,7 @@ namespace PixiEditor.Models.Layers
 
 
         public Int32Rect GetContentDimensions()
         public Int32Rect GetContentDimensions()
         {
         {
-            DoubleCords points = GetEdgePoints();
+            DoubleCoords points = GetEdgePoints();
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
             int biggestX = points.Coords2.X;
             int biggestX = points.Coords2.X;
@@ -557,7 +557,7 @@ namespace PixiEditor.Models.Layers
                 d => d.Value);
                 d => d.Value);
         }
         }
 
 
-        private Tuple<DoubleCords, bool> ExtractBorderData(BitmapPixelChanges pixels)
+        private Tuple<DoubleCoords, bool> ExtractBorderData(BitmapPixelChanges pixels)
         {
         {
             Coordinates firstCords = pixels.ChangedPixels.First().Key;
             Coordinates firstCords = pixels.ChangedPixels.First().Key;
             int minX = firstCords.X;
             int minX = firstCords.X;
@@ -592,8 +592,8 @@ namespace PixiEditor.Models.Layers
                 }
                 }
             }
             }
 
 
-            return new Tuple<DoubleCords, bool>(
-                new DoubleCords(new Coordinates(minX, minY), new Coordinates(maxX, maxY)), clipRequested);
+            return new Tuple<DoubleCoords, bool>(
+                new DoubleCoords(new Coordinates(minX, minY), new Coordinates(maxX, maxY)), clipRequested);
         }
         }
 
 
         private bool IsBorderPixel(Coordinates cords)
         private bool IsBorderPixel(Coordinates cords)
@@ -649,12 +649,12 @@ namespace PixiEditor.Models.Layers
             ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
             ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
         }
         }
 
 
-        private DoubleCords GetEdgePoints()
+        private DoubleCoords GetEdgePoints()
         {
         {
             Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
             Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
             Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
             Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
 
 
-            return new DoubleCords(smallestPixel, biggestPixel);
+            return new DoubleCoords(smallestPixel, biggestPixel);
         }
         }
 
 
         private void ResetOffset(BitmapPixelChanges pixels)
         private void ResetOffset(BitmapPixelChanges pixels)

+ 3 - 3
PixiEditor/Models/Position/CoordinatesCalculator.cs

@@ -13,7 +13,7 @@ namespace PixiEditor.Models.Position
         /// </summary>
         /// </summary>
         /// <param name="startPosition">Top left position of rectangle.</param>
         /// <param name="startPosition">Top left position of rectangle.</param>
         /// <param name="thickness">Thickness of rectangle.</param>
         /// <param name="thickness">Thickness of rectangle.</param>
-        public static DoubleCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
+        public static DoubleCoords CalculateThicknessCenter(Coordinates startPosition, int thickness)
         {
         {
             int x1, x2, y1, y2;
             int x1, x2, y1, y2;
             if (thickness % 2 == 0)
             if (thickness % 2 == 0)
@@ -31,7 +31,7 @@ namespace PixiEditor.Models.Position
                 y1 = y2 - thickness;
                 y1 = y2 - thickness;
             }
             }
 
 
-            return new DoubleCords(new Coordinates(x1, y1), new Coordinates(x2 - 1, y2 - 1));
+            return new DoubleCoords(new Coordinates(x1, y1), new Coordinates(x2 - 1, y2 - 1));
         }
         }
 
 
         public static Coordinates GetCenterPoint(Coordinates startingPoint, Coordinates endPoint)
         public static Coordinates GetCenterPoint(Coordinates startingPoint, Coordinates endPoint)
@@ -78,7 +78,7 @@ namespace PixiEditor.Models.Position
             }
             }
         }
         }
 
 
-        public static IEnumerable<Coordinates> RectangleToCoordinates(DoubleCords coordinates)
+        public static IEnumerable<Coordinates> RectangleToCoordinates(DoubleCoords coordinates)
         {
         {
             return RectangleToCoordinates(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y);
             return RectangleToCoordinates(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y);
         }
         }

+ 14 - 14
PixiEditor/Models/Position/DoubleCords.cs → PixiEditor/Models/Position/DoubleCoords.cs

@@ -1,15 +1,15 @@
-namespace PixiEditor.Models.Position
-{
-    public struct DoubleCords
-    {
-        public DoubleCords(Coordinates cords1, Coordinates cords2)
-        {
-            Coords1 = cords1;
-            Coords2 = cords2;
-        }
-
-        public Coordinates Coords1 { get; set; }
-
-        public Coordinates Coords2 { get; set; }
-    }
+namespace PixiEditor.Models.Position
+{
+    public struct DoubleCoords
+    {
+        public DoubleCoords(Coordinates cords1, Coordinates cords2)
+        {
+            Coords1 = cords1;
+            Coords2 = cords2;
+        }
+
+        public Coordinates Coords1 { get; set; }
+
+        public Coordinates Coords2 { get; set; }
+    }
 }
 }

+ 1 - 1
PixiEditor/Models/Tools/Brushes/CircleBrush.cs

@@ -16,7 +16,7 @@ namespace PixiEditor.Models.Tools.Brushes
             Coordinates topLeft = new Coordinates(coordinates.X - halfSize + modifier, coordinates.Y - halfSize + modifier);
             Coordinates topLeft = new Coordinates(coordinates.X - halfSize + modifier, coordinates.Y - halfSize + modifier);
             Coordinates bottomRight = new Coordinates(coordinates.X + halfSize - 1, coordinates.Y + halfSize - 1);
             Coordinates bottomRight = new Coordinates(coordinates.X + halfSize - 1, coordinates.Y + halfSize - 1);
 
 
-            CircleTool.DrawEllipseFromRect(layer, topLeft, bottomRight, paint.Color, paint.Color, 1, true);
+            CircleTool.DrawEllipseFromCoordinates(layer, topLeft, bottomRight, paint.Color, paint.Color, 1, true);
         }
         }
     }
     }
 }
 }

+ 6 - 6
PixiEditor/Models/Tools/ShapeTool.cs

@@ -11,7 +11,7 @@ namespace PixiEditor.Models.Tools
 {
 {
     public abstract class ShapeTool : BitmapOperationTool
     public abstract class ShapeTool : BitmapOperationTool
     {
     {
-        public static DoubleCords CalculateCoordinatesForShapeRotation(
+        public static DoubleCoords CalculateCoordinatesForShapeRotation(
             Coordinates startingCords,
             Coordinates startingCords,
             Coordinates secondCoordinates)
             Coordinates secondCoordinates)
         {
         {
@@ -19,33 +19,33 @@ namespace PixiEditor.Models.Tools
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, startingCords.Y));
                     new Coordinates(startingCords.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
             }
             }
 
 
             if (startingCords.Y > currentCoordinates.Y)
             if (startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y));
                     new Coordinates(currentCoordinates.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             {
             {
-                return new DoubleCords(
+                return new DoubleCoords(
                     new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y));
                     new Coordinates(startingCords.X, currentCoordinates.Y));
             }
             }
 
 
-            return new DoubleCords(startingCords, secondCoordinates);
+            return new DoubleCoords(startingCords, secondCoordinates);
         }
         }
 
 
         public ShapeTool()
         public ShapeTool()

+ 33 - 19
PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -21,7 +21,7 @@ namespace PixiEditor.Models.Tools.Tools
         private const float CorrectionFactor = 5f; // Initial correction factor
         private const float CorrectionFactor = 5f; // Initial correction factor
 
 
         private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
         private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
-        private List<Coordinates> circleCache = null;
+        private List<DoubleCoords> circleCache = new List<DoubleCoords>();
         private int cachedCircleSize = -1;
         private int cachedCircleSize = -1;
 
 
         public BrightnessTool()
         public BrightnessTool()
@@ -77,29 +77,18 @@ namespace PixiEditor.Models.Tools.Tools
         public void ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
         public void ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
         {
         {
             if (cachedCircleSize != toolSize)
             if (cachedCircleSize != toolSize)
-            {
-                cachedCircleSize = toolSize;
-                circleCache = CircleTool.GenerateMidpointEllipse(toolSize / 2.0, toolSize / 2.0, 0, 0);
-                circleCache.Sort((a, b) => a.Y != b.Y ? a.Y - b.Y : a.X - b.X);
-                for (int i = circleCache.Count - 2; i >= 1; i--)
-                {
-                    if (circleCache[i].Y == circleCache[i - 1].Y && circleCache[i].Y == circleCache[i + 1].Y)
-                    {
-                        circleCache.RemoveAt(i);
-                    }
-                }
-            }
+                UpdateCircleCache(toolSize);
 
 
             int radius = (int)Math.Ceiling(toolSize / 2f);
             int radius = (int)Math.Ceiling(toolSize / 2f);
             layer.DynamicResizeAbsolute(coordinates.X + radius, coordinates.Y + radius, coordinates.X - radius, coordinates.Y - radius);
             layer.DynamicResizeAbsolute(coordinates.X + radius, coordinates.Y + radius, coordinates.X - radius, coordinates.Y - radius);
 
 
-            for (int i = 0; i < circleCache.Count; i += 2)
+            foreach (var pair in circleCache)
             {
             {
-                Coordinates left = circleCache[i];
-                Coordinates right = circleCache[i + 1];
+                Coordinates left = pair.Coords1;
+                Coordinates right = pair.Coords2;
                 int y = left.Y + coordinates.Y;
                 int y = left.Y + coordinates.Y;
 
 
-                for (int x = left.X + coordinates.X; x < right.X + coordinates.X; x++)
+                for (int x = left.X + coordinates.X; x <= right.X + coordinates.X; x++)
                 {
                 {
                     if (Mode == BrightnessMode.Default)
                     if (Mode == BrightnessMode.Default)
                     {
                     {
@@ -117,8 +106,33 @@ namespace PixiEditor.Models.Tools.Tools
                     layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x - layer.OffsetX, y - layer.OffsetY, newColor);
                     layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x - layer.OffsetX, y - layer.OffsetY, newColor);
                 }
                 }
             }
             }
-            Int32Rect rect = new(coordinates.X - radius, coordinates.Y - radius, radius * 2, radius * 2);
-            layer.InvokeLayerBitmapChange(rect);
+            layer.InvokeLayerBitmapChange(new(coordinates.X - radius, coordinates.Y - radius, radius * 2, radius * 2));
+        }
+
+        public void UpdateCircleCache(int newCircleSize)
+        {
+            cachedCircleSize = newCircleSize;
+            DoubleCoords rect = CoordinatesCalculator.CalculateThicknessCenter(new Coordinates(0, 0), newCircleSize);
+            List<Coordinates> circle = CircleTool.GenerateEllipseFromRect(rect);
+            circle.Sort((a, b) => a.Y != b.Y ? a.Y - b.Y : a.X - b.X);
+
+            circleCache.Clear();
+
+            int minX = int.MaxValue;
+            int maxX = int.MinValue;
+            for (int i = 0; i < circle.Count; i++)
+            {
+                if (i >= 1 && circle[i].Y != circle[i - 1].Y)
+                {
+                    int prevY = circle[i - 1].Y;
+                    circleCache.Add(new DoubleCoords(new(minX, prevY), new(maxX, prevY)));
+                    minX = int.MaxValue;
+                    maxX = int.MinValue;
+                }
+                minX = Math.Min(circle[i].X, minX);
+                maxX = Math.Max(circle[i].X, maxX);
+            }
+            circleCache.Add(new DoubleCoords(new(minX, circle[^1].Y), new(maxX, circle[^1].Y)));
         }
         }
     }
     }
 }
 }

+ 66 - 25
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -4,6 +4,7 @@ using PixiEditor.Models.Tools.ToolSettings.Settings;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
@@ -42,30 +43,26 @@ namespace PixiEditor.Models.Tools.Tools
             var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
             var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
             Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
             Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
             SKColor fill = new SKColor(temp.R, temp.G, temp.B, temp.A);
             SKColor fill = new SKColor(temp.R, temp.G, temp.B, temp.A);
-            DrawEllipseFromRect(layer, coordinates[^1], coordinates[0], color, fill, thickness, hasFillColor);
+            DrawEllipseFromCoordinates(layer, coordinates[^1], coordinates[0], color, fill, thickness, hasFillColor);
         }
         }
 
 
-        public static void DrawEllipseFromRect(Layer layer, Coordinates first, Coordinates second,
+        public static void DrawEllipseFromCoordinates(Layer layer, Coordinates first, Coordinates second,
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
         {
         {
-            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(first, second);
+            DoubleCoords corners = CalculateCoordinatesForShapeRotation(first, second);
+            corners.Coords2 = new(corners.Coords2.X, corners.Coords2.Y);
 
 
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             Int32Rect dirtyRect = new Int32Rect(
             Int32Rect dirtyRect = new Int32Rect(
-                fixedCoordinates.Coords1.X - halfThickness,
-                fixedCoordinates.Coords1.Y - halfThickness,
-                fixedCoordinates.Coords2.X + halfThickness * 2 - fixedCoordinates.Coords1.X,
-                fixedCoordinates.Coords2.Y + halfThickness * 2 - fixedCoordinates.Coords1.Y);
+                corners.Coords1.X - halfThickness,
+                corners.Coords1.Y - halfThickness,
+                corners.Coords2.X + halfThickness * 2 - corners.Coords1.X,
+                corners.Coords2.Y + halfThickness * 2 - corners.Coords1.Y);
             layer.DynamicResizeAbsolute(dirtyRect.X + dirtyRect.Width - 1, dirtyRect.Y + dirtyRect.Height - 1, dirtyRect.X, dirtyRect.Y);
             layer.DynamicResizeAbsolute(dirtyRect.X + dirtyRect.Width - 1, dirtyRect.Y + dirtyRect.Height - 1, dirtyRect.X, dirtyRect.Y);
 
 
             using (SKPaint paint = new SKPaint())
             using (SKPaint paint = new SKPaint())
             {
             {
-                float radiusX = (fixedCoordinates.Coords2.X - fixedCoordinates.Coords1.X) / 2.0f;
-                float radiusY = (fixedCoordinates.Coords2.Y - fixedCoordinates.Coords1.Y) / 2.0f;
-                float centerX = (fixedCoordinates.Coords1.X + fixedCoordinates.Coords2.X + 1) / 2.0f;
-                float centerY = (fixedCoordinates.Coords1.Y + fixedCoordinates.Coords2.Y + 1) / 2.0f;
-
-                List<Coordinates> outline = GenerateMidpointEllipse(radiusX, radiusY, centerX, centerY);
+                List<Coordinates> outline = GenerateEllipseFromRect(corners);
                 if (hasFillColor)
                 if (hasFillColor)
                 {
                 {
                     DrawEllipseFill(layer, fillColor, outline);
                     DrawEllipseFill(layer, fillColor, outline);
@@ -126,6 +123,30 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
         }
         }
 
 
+        public static List<Coordinates> GenerateEllipseFromRect(DoubleCoords rect)
+        {
+            float radiusX = (rect.Coords2.X - rect.Coords1.X) / 2.0f;
+            float radiusY = (rect.Coords2.Y - rect.Coords1.Y) / 2.0f;
+            float centerX = (rect.Coords1.X + rect.Coords2.X + 1) / 2.0f;
+            float centerY = (rect.Coords1.Y + rect.Coords2.Y + 1) / 2.0f;
+
+            return GenerateMidpointEllipse(radiusX, radiusY, centerX, centerY);
+        }
+
+        /// <summary>
+        /// Draws an ellipse using it's center and radii
+        ///
+        /// Here is a usage example:
+        /// Let's say you want an ellipse that's 3 pixels wide and 3 pixels tall located in the top right corner of the canvas
+        /// It's center is at (1.5; 1.5). That's in the middle of a pixel
+        /// The radii are both equal to 1. Notice that it's 1 and not 1.5, since we want the ellipse to land in the middle of the pixel, not outside of it.
+        /// See desmos (note the inverted y axis): https://www.desmos.com/calculator/tq9uqg0hcq
+        ///
+        /// Another example:
+        /// 4x4 ellipse in the top right corner of the canvas
+        /// Center is at (2; 2). It's a place where 4 pixels meet
+        /// Both radii are 1.5. Making them 2 would make the ellipse touch the edges of pixels, whereas we want it to stay in the middle
+        /// </summary>
         public static List<Coordinates> GenerateMidpointEllipse(double halfWidth, double halfHeight, double centerX, double centerY)
         public static List<Coordinates> GenerateMidpointEllipse(double halfWidth, double halfHeight, double centerX, double centerY)
         {
         {
             if (halfWidth < 1 || halfHeight < 1)
             if (halfWidth < 1 || halfHeight < 1)
@@ -143,7 +164,7 @@ namespace PixiEditor.Models.Tools.Tools
 
 
             double currentSlope;
             double currentSlope;
 
 
-            // from PI/2 to middle
+            // from PI/2 to PI/4
             do
             do
             {
             {
                 AddRegionPoints(outputCoordinates, currentX, centerX, currentY, centerY);
                 AddRegionPoints(outputCoordinates, currentX, centerX, currentY, centerY);
@@ -165,7 +186,7 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
             while (currentSlope > -1 && currentY - centerY > 0.5);
             while (currentSlope > -1 && currentY - centerY > 0.5);
 
 
-            // from middle to 0
+            // from PI/4 to 0
             while (currentY - centerY >= 0)
             while (currentY - centerY >= 0)
             {
             {
                 AddRegionPoints(outputCoordinates, currentX, centerX, currentY, centerY);
                 AddRegionPoints(outputCoordinates, currentX, centerX, currentY, centerY);
@@ -186,16 +207,21 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             List<Coordinates> coordinates = new List<Coordinates>();
             List<Coordinates> coordinates = new List<Coordinates>();
 
 
-            for (double x = centerX - halfWidth; x <= centerX + halfWidth; x++)
+            int left = (int)Math.Floor(centerX - halfWidth);
+            int top = (int)Math.Floor(centerY - halfHeight);
+            int right = (int)Math.Floor(centerX + halfWidth);
+            int bottom = (int)Math.Floor(centerY + halfHeight);
+
+            for (int x = left; x <= right; x++)
             {
             {
-                coordinates.Add(new Coordinates((int)x, (int)(centerY - halfHeight)));
-                coordinates.Add(new Coordinates((int)x, (int)(centerY + halfHeight)));
+                coordinates.Add(new Coordinates(x, top));
+                coordinates.Add(new Coordinates(x, bottom));
             }
             }
 
 
-            for (double y = centerY - halfHeight + 1; y <= centerY + halfHeight - 1; y++)
+            for (int y = top; y <= bottom; y++)
             {
             {
-                coordinates.Add(new Coordinates((int)(centerX - halfWidth), (int)y));
-                coordinates.Add(new Coordinates((int)(centerX + halfWidth), (int)y));
+                coordinates.Add(new Coordinates(left, y));
+                coordinates.Add(new Coordinates(right, y));
             }
             }
 
 
             return coordinates;
             return coordinates;
@@ -203,10 +229,25 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         private static void AddRegionPoints(List<Coordinates> coordinates, double x, double xc, double y, double yc)
         private static void AddRegionPoints(List<Coordinates> coordinates, double x, double xc, double y, double yc)
         {
         {
-            coordinates.Add(new Coordinates((int)Math.Floor(x), (int)Math.Floor(y)));
-            coordinates.Add(new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(y)));
-            coordinates.Add(new Coordinates((int)Math.Floor(x), (int)Math.Floor(-(y - yc) + yc)));
-            coordinates.Add(new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(-(y - yc) + yc)));
+            int xFloor = (int)Math.Floor(x);
+            int yFloor = (int)Math.Floor(y);
+            int xFloorInv = (int)Math.Floor(-x + 2 * xc);
+            int yFloorInv = (int)Math.Floor(-y + 2 * yc);
+
+            //top and bottom or left and right
+            if (xFloor == xFloorInv || yFloor == yFloorInv)
+            {
+                coordinates.Add(new Coordinates(xFloor, yFloor));
+                coordinates.Add(new Coordinates(xFloorInv, yFloorInv));
+            }
+            //part of the arc
+            else
+            {
+                coordinates.Add(new Coordinates(xFloor, yFloor));
+                coordinates.Add(new Coordinates(xFloorInv, yFloorInv));
+                coordinates.Add(new Coordinates(xFloorInv, yFloor));
+                coordinates.Add(new Coordinates(xFloor, yFloorInv));
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -50,7 +50,7 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, List<Coordinates> coordinates, int thickness)
         public void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, List<Coordinates> coordinates, int thickness)
         {
         {
-            DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
+            DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
 
 
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             Int32Rect dirtyRect = new Int32Rect(
             Int32Rect dirtyRect = new Int32Rect(