|
@@ -1,10 +1,12 @@
|
|
|
-using PixiEditor.Models.Layers;
|
|
|
+using PixiEditor.Helpers.Extensions;
|
|
|
+using PixiEditor.Models.Layers;
|
|
|
using PixiEditor.Models.Position;
|
|
|
using PixiEditor.Models.Tools.ToolSettings.Settings;
|
|
|
using SkiaSharp;
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
+using System.Windows;
|
|
|
using System.Windows.Input;
|
|
|
using System.Windows.Media;
|
|
|
|
|
@@ -38,180 +40,44 @@ namespace PixiEditor.Models.Tools.Tools
|
|
|
public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
|
|
|
{
|
|
|
int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
|
|
|
+ var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
|
|
|
DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
|
|
|
- var outline = CreateEllipse(layer, color, fixedCoordinates.Coords1, fixedCoordinates.Coords2, thickness);
|
|
|
|
|
|
- if (Toolbar.GetSetting<BoolSetting>("Fill").Value)
|
|
|
+ int halfThickness = (int)Math.Ceiling(thickness / 2.0);
|
|
|
+ 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);
|
|
|
+ Int32Rect curLayerRect = new(layer.OffsetX, layer.OffsetY, layer.Width, layer.Height);
|
|
|
+ Int32Rect expanded = dirtyRect.Expand(curLayerRect);
|
|
|
+ layer.DynamicResize(expanded.X + expanded.Width - 1, expanded.Y + expanded.Height - 1, expanded.X, expanded.Y);
|
|
|
+
|
|
|
+ using (SKPaint paint = new SKPaint())
|
|
|
{
|
|
|
- Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
|
|
|
- SKColor fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
|
|
|
- DrawEllipseFill(layer, fillColor, outline);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Draws ellipse for specified coordinates and thickness.
|
|
|
- /// </summary>
|
|
|
- /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
|
|
|
- /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
|
|
|
- /// <param name="thickness">Thickness of ellipse.</param>
|
|
|
- /// <param name="filled">Should ellipse be filled.</param>
|
|
|
- public void CreateEllipse(Layer layer, SKColor color, Coordinates startCoordinates, Coordinates endCoordinates, int thickness, bool filled)
|
|
|
- {
|
|
|
- IEnumerable<Coordinates> outline = CreateEllipse(layer, color, startCoordinates, endCoordinates, thickness);
|
|
|
- if (filled)
|
|
|
- {
|
|
|
- DrawEllipseFill(layer, color, outline);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <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>
|
|
|
- /// <param name="thickness">Thickness of ellipse.</param>
|
|
|
- public IEnumerable<Coordinates> CreateEllipse(Layer layer, SKColor color, Coordinates startCoordinates, Coordinates endCoordinates, int thickness)
|
|
|
- {
|
|
|
- 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;
|
|
|
-
|
|
|
- IEnumerable<Coordinates> ellipse = GenerateMidpointEllipse(layer, color, radiusX, radiusY, centerX, centerY);
|
|
|
- if (thickness > 1)
|
|
|
- {
|
|
|
- ThickenShape(layer, color, ellipse, thickness);
|
|
|
- }
|
|
|
-
|
|
|
- return ellipse;
|
|
|
- }
|
|
|
-
|
|
|
- public List<Coordinates> GenerateMidpointEllipse(Layer layer, SKColor color, double halfWidth, double halfHeight, double centerX, double centerY)
|
|
|
- {
|
|
|
- //using var ctx = layer.LayerBitmap.GetBitmapContext();
|
|
|
-
|
|
|
- if (halfWidth < 1 || halfHeight < 1)
|
|
|
- {
|
|
|
- return DrawFallbackRectangle(layer, color, halfWidth, halfHeight, centerX, centerY);
|
|
|
- }
|
|
|
-
|
|
|
- // 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;
|
|
|
-
|
|
|
- List<Coordinates> outputCoordinates = new List<Coordinates>();
|
|
|
-
|
|
|
- double currentSlope;
|
|
|
-
|
|
|
- // from PI/2 to middle
|
|
|
- do
|
|
|
- {
|
|
|
- outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));
|
|
|
-
|
|
|
- // 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--;
|
|
|
- }
|
|
|
+ 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;
|
|
|
|
|
|
- // 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)
|
|
|
- {
|
|
|
- outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));
|
|
|
-
|
|
|
- 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)
|
|
|
+ if (hasFillColor)
|
|
|
{
|
|
|
- currentX++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return outputCoordinates;
|
|
|
- }
|
|
|
+ Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
|
|
|
+ SKColor fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
|
|
|
|
|
|
- public void DrawEllipseFill(Layer layer, SKColor color, IEnumerable<Coordinates> outlineCoordinates)
|
|
|
- {
|
|
|
- //using var ctx = layer.LayerBitmap.GetBitmapContext();
|
|
|
-
|
|
|
- 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++)
|
|
|
- {
|
|
|
- layer.SetPixel(new Coordinates(j, i), color);
|
|
|
+ paint.Color = fillColor;
|
|
|
+ paint.Style = SKPaintStyle.StrokeAndFill;
|
|
|
+ layer.LayerBitmap.SkiaSurface.Canvas.DrawOval(centerX, centerY, radiusX, radiusY, paint);
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private List<Coordinates> DrawFallbackRectangle(Layer layer, SKColor color, double halfWidth, double halfHeight, double centerX, double centerY)
|
|
|
- {
|
|
|
- //using var ctx = layer.LayerBitmap.GetBitmapContext();
|
|
|
-
|
|
|
- List<Coordinates> coordinates = new List<Coordinates>();
|
|
|
-
|
|
|
- for (double x = centerX - halfWidth; x <= centerX + halfWidth; x++)
|
|
|
- {
|
|
|
- var cords = new Coordinates((int)x, (int)(centerY - halfHeight));
|
|
|
- coordinates.Add(cords);
|
|
|
- layer.SetPixel(cords, color);
|
|
|
|
|
|
- cords = new Coordinates((int)x, (int)(centerY + halfHeight));
|
|
|
- coordinates.Add(cords);
|
|
|
- layer.SetPixel(cords, color);
|
|
|
+ paint.Color = color;
|
|
|
+ paint.StrokeWidth = thickness;
|
|
|
+ paint.Style = SKPaintStyle.Stroke;
|
|
|
+ layer.LayerBitmap.SkiaSurface.Canvas.DrawOval(centerX, centerY, radiusX, radiusY, paint);
|
|
|
}
|
|
|
|
|
|
- for (double y = centerY - halfHeight + 1; y <= centerY + halfHeight - 1; y++)
|
|
|
- {
|
|
|
- var cords = new Coordinates((int)(centerX - halfWidth), (int)y);
|
|
|
- coordinates.Add(cords);
|
|
|
- layer.SetPixel(cords, color);
|
|
|
-
|
|
|
- cords = new Coordinates((int)(centerX + halfWidth), (int)y);
|
|
|
- coordinates.Add(cords);
|
|
|
- layer.SetPixel(cords, color);
|
|
|
- }
|
|
|
-
|
|
|
- return coordinates;
|
|
|
- }
|
|
|
-
|
|
|
- private Coordinates[] DrawRegionPoints(Layer layer, SKColor color, double x, double xc, double y, double yc)
|
|
|
- {
|
|
|
- Coordinates[] outputCoordinates = new Coordinates[4];
|
|
|
- outputCoordinates[0] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(y));
|
|
|
- layer.SetPixel(outputCoordinates[0], color);
|
|
|
- outputCoordinates[1] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(y));
|
|
|
- layer.SetPixel(outputCoordinates[1], color);
|
|
|
- outputCoordinates[2] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(-(y - yc) + yc));
|
|
|
- layer.SetPixel(outputCoordinates[2], color);
|
|
|
- outputCoordinates[3] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(-(y - yc) + yc));
|
|
|
- layer.SetPixel(outputCoordinates[3], color);
|
|
|
+ layer.InvokeLayerBitmapChange(dirtyRect);
|
|
|
|
|
|
- return outputCoordinates;
|
|
|
}
|
|
|
}
|
|
|
}
|