Browse Source

Single object selection with skewing works

flabbet 1 year ago
parent
commit
33d9fe9b3d

+ 34 - 22
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -8,6 +8,7 @@ namespace ChunkyImageLib.DataHolders;
 public struct ShapeCorners
 {
     private const double epsilon = 0.001;
+
     public ShapeCorners(VecD center, VecD size)
     {
         TopLeft = center - size / 2;
@@ -15,6 +16,7 @@ public struct ShapeCorners
         BottomRight = center + size / 2;
         BottomLeft = center + new VecD(-size.X / 2, size.Y / 2);
     }
+
     public ShapeCorners(RectD rect)
     {
         TopLeft = rect.TopLeft;
@@ -22,10 +24,12 @@ public struct ShapeCorners
         BottomRight = rect.BottomRight;
         BottomLeft = rect.BottomLeft;
     }
+
     public VecD TopLeft { get; set; }
     public VecD TopRight { get; set; }
     public VecD BottomLeft { get; set; }
     public VecD BottomRight { get; set; }
+
     public bool IsInverted
     {
         get
@@ -34,9 +38,11 @@ public struct ShapeCorners
             var right = TopRight - BottomRight;
             var bottom = BottomRight - BottomLeft;
             var left = BottomLeft - TopLeft;
-            return Math.Sign(top.Cross(right)) + Math.Sign(right.Cross(bottom)) + Math.Sign(bottom.Cross(left)) + Math.Sign(left.Cross(top)) < 0;
+            return Math.Sign(top.Cross(right)) + Math.Sign(right.Cross(bottom)) + Math.Sign(bottom.Cross(left)) +
+                Math.Sign(left.Cross(top)) < 0;
         }
     }
+
     public bool IsLegal
     {
         get
@@ -48,7 +54,8 @@ public struct ShapeCorners
             var bottom = BottomRight - BottomLeft;
             var left = BottomLeft - TopLeft;
             var topRight = Math.Sign(top.Cross(right));
-            return topRight == Math.Sign(right.Cross(bottom)) && topRight == Math.Sign(bottom.Cross(left)) && topRight == Math.Sign(left.Cross(top));
+            return topRight == Math.Sign(right.Cross(bottom)) && topRight == Math.Sign(bottom.Cross(left)) &&
+                   topRight == Math.Sign(left.Cross(top));
         }
     }
 
@@ -59,31 +66,33 @@ public struct ShapeCorners
     {
         get
         {
-            Span<VecD> lengths = stackalloc[] 
+            Span<VecD> lengths = stackalloc[]
             {
-                TopLeft - TopRight,
-                TopRight - BottomRight,
-                BottomRight - BottomLeft,
-                BottomLeft - TopLeft,
-                TopLeft - BottomRight,
-                TopRight - BottomLeft
+                TopLeft - TopRight, TopRight - BottomRight, BottomRight - BottomLeft, BottomLeft - TopLeft,
+                TopLeft - BottomRight, TopRight - BottomLeft
             };
             foreach (VecD vec in lengths)
             {
                 if (vec.LengthSquared < epsilon * epsilon)
                     return true;
             }
+
             return false;
         }
     }
-    public bool HasNaNOrInfinity => TopLeft.IsNaNOrInfinity() || TopRight.IsNaNOrInfinity() || BottomLeft.IsNaNOrInfinity() || BottomRight.IsNaNOrInfinity();
+
+    public bool HasNaNOrInfinity => TopLeft.IsNaNOrInfinity() || TopRight.IsNaNOrInfinity() ||
+                                    BottomLeft.IsNaNOrInfinity() || BottomRight.IsNaNOrInfinity();
+
     public bool IsRect => Math.Abs((TopLeft - BottomRight).Length - (TopRight - BottomLeft).Length) < epsilon;
     public VecD RectSize => new((TopLeft - TopRight).Length, (TopLeft - BottomLeft).Length);
     public VecD RectCenter => (TopLeft - BottomRight) / 2 + BottomRight;
+
     public double RectRotation =>
-        (TopLeft - TopRight).Cross(TopLeft - BottomLeft) > 0 ?
-        RectSize.CCWAngleTo(BottomRight - TopLeft) :
-        RectSize.CCWAngleTo(BottomLeft - TopRight);
+        (TopLeft - TopRight).Cross(TopLeft - BottomLeft) > 0
+            ? RectSize.CCWAngleTo(BottomRight - TopLeft)
+            : RectSize.CCWAngleTo(BottomLeft - TopRight);
+
     public bool IsSnappedToPixels
     {
         get
@@ -96,6 +105,7 @@ public struct ShapeCorners
                 (BottomRight - BottomRight.Round()).TaxicabLength < epsilon;
         }
     }
+
     public RectD AABBBounds
     {
         get
@@ -120,7 +130,8 @@ public struct ShapeCorners
         var deltaBottomRight = point - BottomRight;
         var deltaBottomLeft = point - BottomLeft;
 
-        if (deltaTopRight.IsNaNOrInfinity() || deltaTopLeft.IsNaNOrInfinity() || deltaBottomRight.IsNaNOrInfinity() || deltaBottomRight.IsNaNOrInfinity())
+        if (deltaTopRight.IsNaNOrInfinity() || deltaTopLeft.IsNaNOrInfinity() || deltaBottomRight.IsNaNOrInfinity() ||
+            deltaBottomRight.IsNaNOrInfinity())
             return false;
 
         var crossTop = Math.Sign(top.Cross(deltaTopLeft));
@@ -186,13 +197,14 @@ public struct ShapeCorners
     }
 
     public static bool operator !=(ShapeCorners left, ShapeCorners right) => !(left == right);
-    public static bool operator == (ShapeCorners left, ShapeCorners right)
+
+    public static bool operator ==(ShapeCorners left, ShapeCorners right)
     {
-        return 
-           left.TopLeft == right.TopLeft &&
-           left.TopRight == right.TopRight &&
-           left.BottomLeft == right.BottomLeft &&
-           left.BottomRight == right.BottomRight;
+        return
+            left.TopLeft == right.TopLeft &&
+            left.TopRight == right.TopRight &&
+            left.BottomLeft == right.BottomLeft &&
+            left.BottomRight == right.BottomRight;
     }
 
     public bool AlmostEquals(ShapeCorners other, double epsilon = 0.001)
@@ -203,7 +215,7 @@ public struct ShapeCorners
             BottomLeft.AlmostEquals(other.BottomLeft, epsilon) &&
             BottomRight.AlmostEquals(other.BottomRight, epsilon);
     }
-    
+
     public bool Intersects(RectD rect)
     {
         // Get all corners
@@ -255,7 +267,7 @@ public struct ShapeCorners
             BottomLeft = transformationMatrix.MapPoint(BottomLeft),
             BottomRight = transformationMatrix.MapPoint(BottomRight)
         };
-        
+
         return corners;
     }
 }

+ 24 - 69
src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelected_UpdateableChange.cs

@@ -17,18 +17,20 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
     private readonly bool drawOnMask;
     private bool keepOriginal;
     private ShapeCorners masterCorners;
-    private VecD originalMasterCornersSize;
 
     private List<MemberTransformationData> memberData;
 
     private VectorPath? originalPath;
     private RectD originalSelectionBounds;
+    private VecD originalSize;
 
     private bool isTransformingSelection;
     private bool hasEnqueudImages = false;
     private int frame;
-    private ShapeCorners lastCorners;
     private bool appliedOnce;
+    
+    private Matrix3X3 globalMatrix;
+    
     private static Paint RegularPaint { get; } = new() { BlendMode = BlendMode.SrcOver };
 
     [GenerateUpdateableChangeActions]
@@ -46,7 +48,6 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
         }
 
         this.masterCorners = masterCorners;
-        lastCorners = masterCorners;
         this.keepOriginal = keepOriginal;
         this.drawOnMask = transformMask;
         this.frame = frame;
@@ -58,19 +59,19 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
             return false;
 
         RectD originalTightBounds = default;
-
+        
         if (target.Selection.SelectionPath is { IsEmpty: false })
         {
             originalPath = new VectorPath(target.Selection.SelectionPath) { FillType = PathFillType.EvenOdd };
             originalTightBounds = originalPath.TightBounds;
-            originalSelectionBounds = masterCorners.AABBBounds;
+            originalSelectionBounds = originalTightBounds;
+            originalSize = originalTightBounds.Size;
             isTransformingSelection = true;
         }
 
         foreach (var member in memberData)
         {
             StructureNode layer = target.FindMemberOrThrow(member.MemberId);
-            originalMasterCornersSize = masterCorners.RectSize;
 
             if (layer is IReadOnlyImageNode)
             {
@@ -81,31 +82,31 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
 
                 if (pathToExtract == null)
                 {
-                    RectI tightBounds = layer.GetTightBounds(frame).Value;
+                    RectI tightBounds = layer.GetTightBounds(frame).GetValueOrDefault();
                     pathToExtract = new VectorPath();
                     pathToExtract.AddRect(tightBounds);
                     targetBounds = pathToExtract.Bounds;
+                    originalSize = tightBounds.Size;
                 }
 
                 member.OriginalPath = pathToExtract;
                 member.OriginalBounds = targetBounds;
-                member.LocalMatrix = OperationHelper.CreateMatrixFromPoints(member.MemberCorners, targetBounds.Size);
                 var extracted = ExtractArea(image, pathToExtract, member.RoundedOriginalBounds.Value);
                 if (extracted.IsT0)
                     continue;
-
+                
                 member.AddImage(extracted.AsT1.image, extracted.AsT1.extractedRect.Pos);
             }
             else if (layer is ITransformableObject transformable)
             {
-                member.LocalMatrix = transformable.TransformationMatrix;
                 RectI tightBounds = layer.GetTightBounds(frame).Value;
                 member.OriginalBounds = (RectD)tightBounds;
+                originalSize = tightBounds.Size;
                 
                 member.AddTransformableObject(transformable, transformable.TransformationMatrix);
             }
         }
-
+        
         return true;
     }
 
@@ -113,15 +114,13 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
     public void Update(ShapeCorners masterCorners, bool keepOriginal)
     {
         this.keepOriginal = keepOriginal;
-        lastCorners = this.masterCorners;
         this.masterCorners = masterCorners;
         
-        Matrix3X3 masterMatrix = OperationHelper.CreateMatrixFromPoints(masterCorners, originalMasterCornersSize);
+        globalMatrix = OperationHelper.CreateMatrixFromPoints(masterCorners, originalSize);
 
         foreach (var member in memberData)
         {
-            ShapeCorners localCorners = MasterToMemberCoords(member.MemberCorners);
-            member.LocalMatrix = OperationHelper.CreateMatrixFromPoints(localCorners, member.OriginalBounds!.Value.Size);
+            member.LocalMatrix = globalMatrix;
         }
     }
 
@@ -173,16 +172,10 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
             }
             else if (member.IsTransformable)
             {
-                ShapeCorners localCorners = MasterToMemberCoords(member.MemberCorners);
-
-                var localMatrix =
-                    OperationHelper.CreateMatrixFromPoints(localCorners, member.OriginalBounds!.Value.Size);
-                member.TransformableObject.TransformationMatrix = localMatrix;
-
-                member.MemberCorners = localCorners;
+                member.TransformableObject.TransformationMatrix = member.LocalMatrix;
 
                 // TODO: this is probably wrong
-                AffectedArea area = GetTranslationAffectedArea(localCorners);
+                AffectedArea area = GetTranslationAffectedArea();
                 infos.Add(new TransformObject_ChangeInfo(member.MemberId, area));
             }
         }
@@ -204,34 +197,25 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
 
         foreach (var member in memberData)
         {
-            ShapeCorners localCorners = MasterToMemberCoords(member.MemberCorners);
-
             if (member.IsImage)
             {
                 ChunkyImage targetImage =
                     DrawingChangeHelper.GetTargetImageOrThrow(target, member.MemberId, drawOnMask, frame);
 
-                member.MemberCorners = localCorners;
-
                 infos.Add(DrawingChangeHelper.CreateAreaChangeInfo(member.MemberId,
                         DrawImage(member, targetImage), drawOnMask)
                     .AsT1);
             }
             else if (member.IsTransformable)
             {
-                var localMatrix =
-                    OperationHelper.CreateMatrixFromPoints(localCorners, member.OriginalBounds!.Value.Size);
-                member.TransformableObject.TransformationMatrix = localMatrix;
-
-                member.MemberCorners = localCorners;
+                member.TransformableObject.TransformationMatrix = member.LocalMatrix; 
 
                 // TODO: this is probably wrong
-                AffectedArea translationAffectedArea = GetTranslationAffectedArea(localCorners);
+                AffectedArea translationAffectedArea = GetTranslationAffectedArea();
                 infos.Add(new TransformObject_ChangeInfo(member.MemberId, translationAffectedArea));
             }
         }
 
-
         if (isTransformingSelection)
         {
             infos.Add(SelectionChangeHelper.DoSelectionTransform(target, originalPath!, originalSelectionBounds,
@@ -241,34 +225,6 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
         return infos;
     }
 
-    private ShapeCorners MasterToMemberCoords(ShapeCorners memberCorner)
-    {
-        VecD posDiff = masterCorners.RectCenter - lastCorners.RectCenter;
-        VecD sizeDiff = masterCorners.RectSize - lastCorners.RectSize;
-        double rotDiff = masterCorners.RectRotation - lastCorners.RectRotation;
-
-        ShapeCorners rotatedCorners =
-            new ShapeCorners(memberCorner.RectCenter + posDiff, memberCorner.RectSize + sizeDiff)
-                .AsRotated(memberCorner.RectRotation, memberCorner.RectCenter + posDiff)
-                .AsRotated(rotDiff, masterCorners.RectCenter);
-
-        /*VecD[] cornersDiff = new VecD[]
-        {
-            masterCorners.TopLeft - lastCorners.TopLeft, masterCorners.TopRight - lastCorners.TopRight,
-            masterCorners.BottomLeft - lastCorners.BottomLeft, masterCorners.BottomRight - lastCorners.BottomRight
-        };
-        
-        ShapeCorners rotatedCorners = new ShapeCorners()
-        {
-            TopLeft = memberCorner.TopLeft + cornersDiff[0],
-            TopRight = memberCorner.TopRight + cornersDiff[1],
-            BottomLeft = memberCorner.BottomLeft + cornersDiff[2],
-            BottomRight = memberCorner.BottomRight + cornersDiff[3]
-        };*/
-
-        return rotatedCorners;
-    }
-
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         List<IChangeInfo> infos = new();
@@ -290,7 +246,7 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
                 member.TransformableObject.TransformationMatrix = member.OriginalMatrix!.Value;
 
                 //TODO this is probably wrong
-                AffectedArea area = GetTranslationAffectedArea(member.MemberCorners);
+                AffectedArea area = GetTranslationAffectedArea();
                 infos.Add(new TransformObject_ChangeInfo(member.MemberId, area));
             }
         }
@@ -319,9 +275,9 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
         }
     }
 
-    private AffectedArea GetTranslationAffectedArea(ShapeCorners originalCorners)
+    private AffectedArea GetTranslationAffectedArea()
     {
-        RectI oldBounds = (RectI)originalCorners.AABBBounds.RoundOutwards();
+        RectI oldBounds = (RectI)masterCorners.AABBBounds.RoundOutwards();
 
         HashSet<VecI> chunks = new();
         VecI topLeftChunk = new VecI((int)oldBounds.Left / ChunkyImage.FullChunkSize,
@@ -346,8 +302,6 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
 
         memberImage.CancelChanges();
 
-        Matrix3X3 globalMatrix = data.LocalMatrix!.Value;
-
         var originalPos = data.ImagePos!.Value;
 
         if (!keepOriginal)
@@ -367,7 +321,7 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
 class MemberTransformationData : IDisposable
 {
     public Guid MemberId { get; }
-    public ShapeCorners MemberCorners { get; set; }
+    public ShapeCorners MemberCorners { get; init; }
 
     public ITransformableObject? TransformableObject { get; private set; }
     public Matrix3X3? OriginalMatrix { get; private set; }
@@ -380,7 +334,7 @@ class MemberTransformationData : IDisposable
     public bool IsImage => Image != null;
     public bool IsTransformable => TransformableObject != null;
     public RectI? RoundedOriginalBounds => (RectI?)OriginalBounds?.RoundOutwards();
-    public Matrix3X3? LocalMatrix { get; set; }
+    public Matrix3X3 LocalMatrix { get; set; }
 
     public MemberTransformationData(Guid memberId)
     {
@@ -391,6 +345,7 @@ class MemberTransformationData : IDisposable
     {
         TransformableObject = transformableObject;
         OriginalMatrix = originalMatrix;
+        LocalMatrix = originalMatrix;
     }
 
     public void AddImage(Surface img, VecI extractedRectPos)

+ 2 - 1
src/PixiEditor/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Globalization;
 using System.Linq;
 using System.Windows.Input;
 using Avalonia;
@@ -276,7 +277,7 @@ internal class TransformOverlay : Overlay
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(bottomLeft));
         context.DrawLine(blackDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
-
+        
         // corner anchors
 
         centerHandle.Position = VecD.Zero;