|
@@ -1,3 +1,4 @@
|
|
|
+using PixiEditor.Helpers;
|
|
|
using PixiEditor.Helpers.Extensions;
|
|
|
using PixiEditor.Models.Layers;
|
|
|
using PixiEditor.Models.Position;
|
|
@@ -6,8 +7,10 @@ using PixiEditor.Models.Tools.Tools;
|
|
|
using SkiaSharp;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Windows;
|
|
|
+using System.Windows.Input;
|
|
|
|
|
|
namespace PixiEditor.Models.ImageManipulation
|
|
|
{
|
|
@@ -48,9 +51,8 @@ namespace PixiEditor.Models.ImageManipulation
|
|
|
List<Coordinates> output)
|
|
|
{
|
|
|
DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
|
|
|
-
|
|
|
- CreateEllipse(fixedCoordinates.Coords1, fixedCoordinates.Coords2, output);
|
|
|
-
|
|
|
+
|
|
|
+ EllipseGenerator.GenerateEllipseFromRect(fixedCoordinates, output);
|
|
|
if (fill)
|
|
|
{
|
|
|
CalculateFillForEllipse(output);
|
|
@@ -195,115 +197,18 @@ namespace PixiEditor.Models.ImageManipulation
|
|
|
private static void CalculateFillForEllipse(List<Coordinates> outlineCoordinates)
|
|
|
{
|
|
|
if (!outlineCoordinates.Any())
|
|
|
- {
|
|
|
return;
|
|
|
- }
|
|
|
-
|
|
|
- int bottom = outlineCoordinates.Max(x => x.Y);
|
|
|
- int top = outlineCoordinates.Min(x => x.Y);
|
|
|
- for (int i = top + 1; i < bottom; i++)
|
|
|
- {
|
|
|
- IEnumerable<Coordinates> rowCords = outlineCoordinates.Where(x => x.Y == i);
|
|
|
- int right = rowCords.Max(x => x.X);
|
|
|
- int left = rowCords.Min(x => x.X);
|
|
|
- for (int j = left + 1; j < right; j++)
|
|
|
- {
|
|
|
- outlineCoordinates.Add(new Coordinates(j, i));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Calculates ellipse points for specified coordinates and thickness.
|
|
|
- /// </summary>
|
|
|
- /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
|
|
|
- /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
|
|
|
- private static void CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, List<Coordinates> output)
|
|
|
- {
|
|
|
- double radiusX = (endCoordinates.X - startCoordinates.X) / 2.0;
|
|
|
- double radiusY = (endCoordinates.Y - startCoordinates.Y) / 2.0;
|
|
|
- double centerX = (startCoordinates.X + endCoordinates.X + 1) / 2.0;
|
|
|
- double centerY = (startCoordinates.Y + endCoordinates.Y + 1) / 2.0;
|
|
|
-
|
|
|
- MidpointEllipse(radiusX, radiusY, centerX, centerY, output);
|
|
|
- }
|
|
|
-
|
|
|
- private static void MidpointEllipse(double halfWidth, double halfHeight, double centerX, double centerY, List<Coordinates> output)
|
|
|
- {
|
|
|
- if (halfWidth < 1 || halfHeight < 1)
|
|
|
- {
|
|
|
- FallbackRectangle(halfWidth, halfHeight, centerX, centerY, output);
|
|
|
- }
|
|
|
-
|
|
|
- // ellipse formula: halfHeight^2 * x^2 + halfWidth^2 * y^2 - halfHeight^2 * halfWidth^2 = 0
|
|
|
|
|
|
- // Make sure we are always at the center of a pixel
|
|
|
- double currentX = Math.Ceiling(centerX - 0.5) + 0.5;
|
|
|
- double currentY = centerY + halfHeight;
|
|
|
-
|
|
|
- double currentSlope;
|
|
|
-
|
|
|
- // from PI/2 to middle
|
|
|
- do
|
|
|
+ var lines = EllipseGenerator.SplitEllipseIntoLines(outlineCoordinates);
|
|
|
+ foreach (var line in lines)
|
|
|
{
|
|
|
- GetRegionPoints(currentX, centerX, currentY, centerY, output);
|
|
|
-
|
|
|
- // calculate next pixel coords
|
|
|
- currentX++;
|
|
|
-
|
|
|
- if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX, 2)) +
|
|
|
- (Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY - 0.5, 2)) -
|
|
|
- (Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) >= 0)
|
|
|
- {
|
|
|
- currentY--;
|
|
|
- }
|
|
|
-
|
|
|
- // calculate how far we've advanced
|
|
|
- double derivativeX = 2 * Math.Pow(halfHeight, 2) * (currentX - centerX);
|
|
|
- double derivativeY = 2 * Math.Pow(halfWidth, 2) * (currentY - centerY);
|
|
|
- currentSlope = -(derivativeX / derivativeY);
|
|
|
- }
|
|
|
- while (currentSlope > -1 && currentY - centerY > 0.5);
|
|
|
-
|
|
|
- // from middle to 0
|
|
|
- while (currentY - centerY >= 0)
|
|
|
- {
|
|
|
- GetRegionPoints(currentX, centerX, currentY, centerY, output);
|
|
|
-
|
|
|
- currentY--;
|
|
|
- if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX + 0.5, 2)) +
|
|
|
- (Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY, 2)) -
|
|
|
- (Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) < 0)
|
|
|
+ for (int i = line.Coords1.X; i <= line.Coords2.X; i++)
|
|
|
{
|
|
|
- currentX++;
|
|
|
+ outlineCoordinates.Add(new Coordinates(i, line.Coords1.Y));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static void FallbackRectangle(double halfWidth, double halfHeight, double centerX, double centerY, List<Coordinates> output)
|
|
|
- {
|
|
|
- for (double x = centerX - halfWidth; x <= centerX + halfWidth; x++)
|
|
|
- {
|
|
|
- output.Add(new Coordinates((int)x, (int)(centerY - halfHeight)));
|
|
|
- output.Add(new Coordinates((int)x, (int)(centerY + halfHeight)));
|
|
|
- }
|
|
|
-
|
|
|
- for (double y = centerY - halfHeight + 1; y <= centerY + halfHeight - 1; y++)
|
|
|
- {
|
|
|
- output.Add(new Coordinates((int)(centerX - halfWidth), (int)y));
|
|
|
- output.Add(new Coordinates((int)(centerX + halfWidth), (int)y));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void GetRegionPoints(double x, double xc, double y, double yc, List<Coordinates> output)
|
|
|
- {
|
|
|
- output.Add(new Coordinates((int)Math.Floor(x), (int)Math.Floor(y)));
|
|
|
- output.Add(new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(y)));
|
|
|
- output.Add(new Coordinates((int)Math.Floor(x), (int)Math.Floor(-(y - yc) + yc)));
|
|
|
- output.Add(new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(-(y - yc) + yc)));
|
|
|
- }
|
|
|
-
|
|
|
private static void CalculateRectangleFillNonAlloc(Coordinates start, Coordinates end, int thickness, List<Coordinates> output)
|
|
|
{
|
|
|
int offset = (int)Math.Ceiling(thickness / 2f);
|