|
@@ -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));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|