Skip to content

Commit

Permalink
Update CircleTool.cs
Browse files Browse the repository at this point in the history
  • Loading branch information
flabbet committed Oct 10, 2021
1 parent 0034798 commit e753528
Showing 1 changed file with 30 additions and 164 deletions.
194 changes: 30 additions & 164 deletions PixiEditor/Models/Tools/Tools/CircleTool.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -38,180 +40,44 @@ public override void OnKeyUp(KeyEventArgs e)
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;
}
}
}

0 comments on commit e753528

Please sign in to comment.