Browse Source

rotated ellipse alg wip and draw ellipse on each brush point <= 10width

flabbet 11 months ago
parent
commit
d3b8f94447

+ 2 - 1
src/ChunkyImageLib/ChunkyImage.cs

@@ -580,12 +580,13 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
 
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
     public void EnqueueDrawEllipse(RectI location, Color strokeColor, Color fillColor, int strokeWidth,
+        double rotationRad = 0,
         Paint? paint = null)
     {
         lock (lockObject)
         {
             ThrowIfDisposed();
-            EllipseOperation operation = new(location, strokeColor, fillColor, strokeWidth, paint);
+            EllipseOperation operation = new(location, strokeColor, fillColor, strokeWidth, rotationRad, paint);
             EnqueueOperation(operation);
         }
     }

+ 32 - 7
src/ChunkyImageLib/Operations/EllipseHelper.cs

@@ -3,12 +3,14 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Numerics;
 
 namespace ChunkyImageLib.Operations;
+
 public class EllipseHelper
 {
     /// <summary>
     /// Separates the ellipse's inner area into a bunch of horizontal lines and one big rectangle for drawing.
     /// </summary>
-    public static (List<VecI> lines, RectI rect) SplitEllipseFillIntoRegions(IReadOnlyList<VecI> ellipse, RectI ellipseBounds)
+    public static (List<VecI> lines, RectI rect) SplitEllipseFillIntoRegions(IReadOnlyList<VecI> ellipse,
+        RectI ellipseBounds)
     {
         if (ellipse.Count == 0)
             return (new(), RectI.Empty);
@@ -51,9 +53,10 @@ public class EllipseHelper
                 }
             }
         }
+
         return (lines, inscribedRect);
     }
-    
+
     /// <summary>
     /// Splits the ellipse into a bunch of horizontal lines.
     /// The resulting list contains consecutive pairs of <see cref="VecI"/>s, each pair has one for the start of the line and one for the end.
@@ -79,24 +82,28 @@ public class EllipseHelper
                 minX = int.MaxValue;
                 maxX = int.MinValue;
             }
+
             minX = Math.Min(point.X, minX);
             maxX = Math.Max(point.X, maxX);
             prev = point;
         }
+
         if (prev != null)
         {
             lines.Add(new(minX, prev.Value.Y));
             lines.Add(new(maxX, prev.Value.Y));
         }
+
         return lines;
     }
-    
-    public static List<VecI> GenerateEllipseFromRect(RectI rect)
+
+    public static List<VecI> GenerateEllipseFromRect(RectI rect, double rotationRad = 0)
     {
         if (rect.IsZeroOrNegativeArea)
             return new();
         float radiusX = (rect.Width - 1) / 2.0f;
         float radiusY = (rect.Height - 1) / 2.0f;
+        //TODO: Implement rotation
         return GenerateMidpointEllipse(radiusX, radiusY, rect.Center.X, rect.Center.Y);
     }
 
@@ -156,8 +163,7 @@ public class EllipseHelper
             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);
+        } while (currentSlope > -1 && currentY - centerY > 0.5);
 
         // from PI/4 to 0
         while (currentY - centerY >= 0)
@@ -176,7 +182,26 @@ public class EllipseHelper
         return listToFill;
     }
 
-    private static void AddFallbackRectangle(double halfWidth, double halfHeight, double centerX, double centerY, List<VecI> coordinates)
+    /*private static List<VecI> GenerateMidpointEllipseRotated(double halfWidth, double halfHeight, double centerX,
+        double centerY, double rotationRad)
+    {
+        var listToFill = new List<VecI>();
+        if (halfWidth < 1 || halfHeight < 1)
+        {
+            AddFallbackRectangle(halfWidth, halfHeight, centerX, centerY, listToFill);
+            return listToFill;
+        }
+        
+        // formula ((x - h)cos(tetha) + (y - k)sin(tetha))^2 / a^2 + (-(x-h)sin(tetha)+(y-k)cos(tetha))^2 / b^2 = 1
+        
+        double cos = Math.Cos(rotationRad);
+        double sin = Math.Sin(rotationRad);
+        
+        
+    }*/
+
+    private static void AddFallbackRectangle(double halfWidth, double halfHeight, double centerX, double centerY,
+        List<VecI> coordinates)
     {
         int left = (int)Math.Floor(centerX - halfWidth);
         int top = (int)Math.Floor(centerY - halfHeight);

+ 5 - 3
src/ChunkyImageLib/Operations/EllipseOperation.cs

@@ -15,6 +15,7 @@ internal class EllipseOperation : IMirroredDrawOperation
     private readonly Color strokeColor;
     private readonly Color fillColor;
     private readonly int strokeWidth;
+    private readonly double rotation;
     private readonly Paint paint;
     private bool init = false;
     private VectorPath? outerPath;
@@ -23,12 +24,13 @@ internal class EllipseOperation : IMirroredDrawOperation
     private Point[]? ellipseFill;
     private RectI? ellipseFillRect;
 
-    public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, Paint? paint = null)
+    public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, double rotationRad, Paint? paint = null)
     {
         this.location = location;
         this.strokeColor = strokeColor;
         this.fillColor = fillColor;
         this.strokeWidth = strokeWidth;
+        this.rotation = rotationRad;
         this.paint = paint?.Clone() ?? new Paint();
     }
 
@@ -37,7 +39,7 @@ internal class EllipseOperation : IMirroredDrawOperation
         init = true;
         if (strokeWidth == 1)
         {
-            var ellipseList = EllipseHelper.GenerateEllipseFromRect(location);
+            var ellipseList = EllipseHelper.GenerateEllipseFromRect(location, rotation);
             ellipse = ellipseList.Select(a => new Point(a)).ToArray();
             if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
             {
@@ -113,7 +115,7 @@ internal class EllipseOperation : IMirroredDrawOperation
             newLocation = (RectI)newLocation.ReflectX((double)verAxisX).Round();
         if (horAxisY is not null)
             newLocation = (RectI)newLocation.ReflectY((double)horAxisY).Round();
-        return new EllipseOperation(newLocation, strokeColor, fillColor, strokeWidth, paint);
+        return new EllipseOperation(newLocation, strokeColor, fillColor, strokeWidth, rotation, paint);
     }
 
     public void Dispose()

+ 1 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -28,18 +28,12 @@ public class EllipseVectorData : ShapeVectorData
         using ChunkyImage img = new ChunkyImage(imageSize);
         RectI rect = new RectI(0, 0, imageSize.X, imageSize.Y); 
         
-        img.EnqueueDrawEllipse(rect, StrokeColor, FillColor, StrokeWidth);
+        img.EnqueueDrawEllipse(rect, StrokeColor, FillColor, StrokeWidth, RotationRadians);
         img.CommitChanges();
         
         VecI topLeft = new VecI((int)(Position.X - Radius.X), (int)(Position.Y - Radius.Y));
 
-        drawingSurface.Canvas.Save();
-        
-        drawingSurface.Canvas.RotateRadians((float)RotationRadians, (float)Position.X, (float)Position.Y);
-        
         img.DrawMostUpToDateRegionOn(rect, ChunkResolution.Full, drawingSurface, topLeft);
-        
-        drawingSurface.Canvas.Restore();
     }
 
     public override bool IsValid()

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/ChangeBrightness_UpdateableChange.cs

@@ -31,7 +31,7 @@ internal class ChangeBrightness_UpdateableChange : UpdateableChange
         this.frame = frame;
         // TODO: pos is unused, check if it should be added to positions
         
-        ellipseLines = EllipseHelper.SplitEllipseIntoLines((EllipseHelper.GenerateEllipseFromRect(new RectI(0, 0, strokeWidth, strokeWidth))));
+        ellipseLines = EllipseHelper.SplitEllipseIntoLines((EllipseHelper.GenerateEllipseFromRect(new RectI(0, 0, strokeWidth, strokeWidth), 0)));
     }
 
     [UpdateChangeMethod]

+ 15 - 5
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -1,4 +1,5 @@
-using PixiEditor.DrawingApi.Core.ColorsImpl;
+using ChunkyImageLib.Operations;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
@@ -63,10 +64,19 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         {
             image.EnqueueDrawBresenhamLine(from, to, color, BlendMode.Src);
         }
+        else if (strokeWidth <= 10)
+        {
+            var bresenham = BresenhamLineHelper.GetBresenhamLine(from, to);
+            foreach (var point in bresenham)
+            {
+                var rect = new RectI(point - new VecI(strokeWidth / 2), new VecI(strokeWidth));
+                image.EnqueueDrawEllipse(rect, color, color, 1, 0, srcPaint);
+            }
+        }
         else
         {
             var rect = new RectI(to - new VecI(strokeWidth / 2), new VecI(strokeWidth));
-            image.EnqueueDrawEllipse(rect, color, color, 1, srcPaint);
+            image.EnqueueDrawEllipse(rect, color, color, 1, 0, srcPaint);
             image.EnqueueDrawSkiaLine(from, to, StrokeCap.Butt, strokeWidth, color, BlendMode.Src);
         }
         var affChunks = image.FindAffectedArea(opCount);
@@ -85,13 +95,13 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             else
             {
                 var rect = new RectI(points[0] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
-                targetImage.EnqueueDrawEllipse(rect, color, color, 1, srcPaint);
+                targetImage.EnqueueDrawEllipse(rect, color, color, 1, 0, srcPaint);
             }
             return;
         }
 
         var firstRect = new RectI(points[0] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
-        targetImage.EnqueueDrawEllipse(firstRect, color, color, 1, srcPaint);
+        targetImage.EnqueueDrawEllipse(firstRect, color, color, 1, 0, srcPaint);
 
         for (int i = 1; i < points.Count; i++)
         {
@@ -102,7 +112,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             else
             {
                 var rect = new RectI(points[i] - new VecI(strokeWidth / 2), new VecI(strokeWidth));
-                targetImage.EnqueueDrawEllipse(rect, color, color, 1, srcPaint);
+                targetImage.EnqueueDrawEllipse(rect, color, color, 1, 0, srcPaint);
                 targetImage.EnqueueDrawSkiaLine(points[i - 1], points[i], StrokeCap.Butt, strokeWidth, color, BlendMode.Src);
             }
         }

+ 3 - 0
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -612,6 +612,9 @@ internal class MemberPreviewUpdater
     {
         QueueRender(() =>
         {
+            if(surface.IsDisposed)
+                return;
+            
             surface.DrawingSurface.Canvas.Save();
             surface.DrawingSurface.Canvas.Scale(scaling);
             surface.DrawingSurface.Canvas.Translate(-position);

+ 1 - 1
src/PixiEditor/Views/Overlays/BrushShapeOverlay/BrushShapeOverlay.cs

@@ -165,7 +165,7 @@ internal class BrushShapeOverlay : Overlay
     private static PathGeometry ConstructEllipseOutline(RectI rectangle)
     {
         var center = rectangle.Center;
-        var points = EllipseHelper.GenerateEllipseFromRect(rectangle);
+        var points = EllipseHelper.GenerateEllipseFromRect(rectangle, 0);
         points.Sort((vec, vec2) => Math.Sign((vec - center).Angle - (vec2 - center).Angle));
         List<VecI> finalPoints = new();
         for (int i = 0; i < points.Count; i++)