Browse Source

Reference layer done

Equbuxu 2 years ago
parent
commit
2578e5f10f

+ 26 - 1
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -3,6 +3,7 @@
 namespace ChunkyImageLib.DataHolders;
 public struct ShapeCorners
 {
+    private const double epsilon = 0.001;
     public ShapeCorners(VecD center, VecD size)
     {
         TopLeft = center - size / 2;
@@ -46,8 +47,32 @@ public struct ShapeCorners
             return topRight == Math.Sign(right.Cross(bottom)) && topRight == Math.Sign(bottom.Cross(left)) && topRight == Math.Sign(left.Cross(top));
         }
     }
+
+    /// <summary>
+    /// Checks if two or more corners are in the same position
+    /// </summary>
+    public bool IsPartiallyDegenerate
+    {
+        get
+        {
+            Span<VecD> lengths = stackalloc[] {
+                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 IsRect => Math.Abs((TopLeft - BottomRight).Length - (TopRight - BottomLeft).Length) < 0.001;
+    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 =>

+ 0 - 3
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/TransformReferenceLayer_ChangeInfo.cs

@@ -1,3 +0,0 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
-
-public record class TransformReferenceLayer_ChangeInfo(ShapeCorners Shape) : IChangeInfo;

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Root/ReferenceLayerChanges/TransformReferenceLayer_UpdateableChange.cs

@@ -1,4 +1,4 @@
-using PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+using PixiEditor.ChangeableDocument.ChangeInfos.Root.ReferenceLayerChangeInfos;
 
 namespace PixiEditor.ChangeableDocument.Changes.Root.ReferenceLayerChanges;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Enums/ResizeAnchorEx.cs

@@ -2,7 +2,7 @@
 
 namespace PixiEditor.ChangeableDocument.Enums;
 
-internal static class ResizeAnchorEx
+public static class ResizeAnchorEx
 {
     public static VecI FindOffsetFor(this ResizeAnchor anchor, VecI imageSize, VecI newImageSize)
     {

+ 0 - 1
src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs

@@ -15,7 +15,6 @@ using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.Enums;
 using PixiEditor.ViewModels.SubViewModels.Document;
-using TransformReferenceLayer_ChangeInfo = PixiEditor.ChangeableDocument.ChangeInfos.Root.ReferenceLayerChangeInfos.TransformReferenceLayer_ChangeInfo;
 
 namespace PixiEditor.Models.DocumentModels;
 #nullable enable

+ 58 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -1,8 +1,11 @@
 using System.Collections.Immutable;
+using System.Windows.Interop;
+using System.Windows.Shapes;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Actions.Undo;
+using PixiEditor.ChangeableDocument.ChangeInfos.Root.ReferenceLayerChangeInfos;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
@@ -12,6 +15,7 @@ using PixiEditor.Models.Enums;
 using PixiEditor.Models.Position;
 using PixiEditor.Parser;
 using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.Views.UserControls.TransformOverlay;
 
 namespace PixiEditor.Models.DocumentModels.Public;
 #nullable enable
@@ -130,6 +134,21 @@ internal class DocumentOperationsModule
     {
         if (Internals.ChangeController.IsChangeActive || newSize.X > 9999 || newSize.Y > 9999 || newSize.X < 1 || newSize.Y < 1)
             return;
+
+        if (Document.ReferenceLayerViewModel.ReferenceBitmap is not null)
+        {
+            VecI offset = anchor.FindOffsetFor(Document.SizeBindable, newSize);
+            ShapeCorners curShape = Document.ReferenceLayerViewModel.ReferenceShapeBindable;
+            ShapeCorners offsetCorners = new ShapeCorners()
+            {
+                TopLeft = curShape.TopLeft + offset,
+                TopRight = curShape.TopRight + offset,
+                BottomLeft = curShape.BottomLeft + offset,
+                BottomRight = curShape.BottomRight + offset,
+            };
+            Internals.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(offsetCorners), new EndTransformReferenceLayer_Action());
+        }
+
         Internals.ActionAccumulator.AddFinishedActions(new ResizeCanvas_Action(newSize, anchor));
     }
 
@@ -137,6 +156,21 @@ internal class DocumentOperationsModule
     {
         if (Internals.ChangeController.IsChangeActive || newSize.X > 9999 || newSize.Y > 9999 || newSize.X < 1 || newSize.Y < 1)
             return;
+
+        if (Document.ReferenceLayerViewModel.ReferenceBitmap is not null)
+        {
+            VecD scale = ((VecD)newSize).Divide(Document.SizeBindable);
+            ShapeCorners curShape = Document.ReferenceLayerViewModel.ReferenceShapeBindable;
+            ShapeCorners offsetCorners = new ShapeCorners()
+            {
+                TopLeft = curShape.TopLeft.Multiply(scale),
+                TopRight = curShape.TopRight.Multiply(scale),
+                BottomLeft = curShape.BottomLeft.Multiply(scale),
+                BottomRight = curShape.BottomRight.Multiply(scale),
+            };
+            Internals.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(offsetCorners), new EndTransformReferenceLayer_Action());
+        }
+
         Internals.ActionAccumulator.AddFinishedActions(new ResizeImage_Action(newSize, resampling));
     }
 
@@ -263,11 +297,13 @@ internal class DocumentOperationsModule
         Internals.ActionAccumulator.AddFinishedActions(new CenterContent_Action(structureMembers.ToList()));
     }
 
-    public void ImportReferenceLayer(ImmutableArray<byte> imagePbgra32Bytes, VecI imageSize, ShapeCorners corners)
+    public void ImportReferenceLayer(ImmutableArray<byte> imagePbgra32Bytes, VecI imageSize)
     {
         if (Internals.ChangeController.IsChangeActive)
             return;
 
+        RectD referenceImageRect = new RectD(VecD.Zero, Document.SizeBindable).AspectFit(new RectD(VecD.Zero, imageSize));
+        ShapeCorners corners = new ShapeCorners(referenceImageRect);
         Internals.ActionAccumulator.AddFinishedActions(new SetReferenceLayer_Action(corners, imagePbgra32Bytes, imageSize));
     }
 
@@ -278,4 +314,25 @@ internal class DocumentOperationsModule
 
         Internals.ActionAccumulator.AddFinishedActions(new DeleteReferenceLayer_Action());
     }
+
+    public void TransformReferenceLayer()
+    {
+        if (Document.ReferenceLayerViewModel.ReferenceBitmap is null || Internals.ChangeController.IsChangeActive)
+            return;
+        Internals.ChangeController.TryStartExecutor(new TransformReferenceLayerExecutor());
+    }
+
+    public void ResetReferenceLayerPosition()
+    {
+        if (Document.ReferenceLayerViewModel.ReferenceBitmap is null || Internals.ChangeController.IsChangeActive)
+            return;
+
+        VecD size = new(Document.ReferenceLayerViewModel.ReferenceBitmap.PixelWidth, Document.ReferenceLayerViewModel.ReferenceBitmap.PixelHeight);
+        RectD referenceImageRect = new RectD(VecD.Zero, Document.SizeBindable).AspectFit(new RectD(VecD.Zero, size));
+        ShapeCorners corners = new ShapeCorners(referenceImageRect);
+        Internals.ActionAccumulator.AddFinishedActions(
+            new TransformReferenceLayer_Action(corners),
+            new EndTransformReferenceLayer_Action()
+            );
+    }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EllipseToolExecutor.cs

@@ -24,7 +24,7 @@ internal class EllipseToolExecutor : ShapeToolExecutor<EllipseToolViewModel>
     }
 
     public override ExecutorType Type => ExecutorType.ToolLinked;
-    protected override DocumentTransformMode TransformMode => DocumentTransformMode.NoRotation;
+    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_NoRotate_NoShear_NoPerspective;
     protected override void DrawShape(VecI currentPos, bool firstDraw) => DrawEllipseOrCircle(currentPos, firstDraw);
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners) =>

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -35,7 +35,7 @@ internal class PasteImageExecutor : UpdateableChangeExecutor
 
         ShapeCorners corners = new(new RectD(pos, image.Size));
         internals!.ActionAccumulator.AddActions(new PasteImage_Action(image, corners, memberGuid, false, drawOnMask));
-        document.TransformViewModel.ShowTransform(DocumentTransformMode.Freeform, true, corners);
+        document.TransformViewModel.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_Perspective, true, corners);
 
         return ExecutionState.Success;
     }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ShapeToolExecutor.cs

@@ -54,7 +54,7 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
     protected abstract void DrawShape(VecI currentPos, bool firstDraw);
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected abstract IAction EndDrawAction();
-    protected virtual DocumentTransformMode TransformMode => DocumentTransformMode.Rotation;
+    protected virtual DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
 
     protected RectI GetSquaredCoordinates(VecI startPos, VecI curPos)
     {

+ 40 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.Models.Enums;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor
+{
+    public override ExecutionState Start()
+    {
+        if (document!.ReferenceLayerViewModel.ReferenceBitmap is null)
+            return ExecutionState.Error;
+
+        ShapeCorners corners = document.ReferenceLayerViewModel.ReferenceShapeBindable;
+        document.TransformViewModel.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_NoPerspective, true, corners);
+        internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
+        return ExecutionState.Success;
+    }
+
+    public override void OnTransformMoved(ShapeCorners corners)
+    {
+        internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
+    }
+
+    public override void OnTransformApplied()
+    {
+        internals!.ActionAccumulator.AddFinishedActions(new EndTransformReferenceLayer_Action());
+        document!.TransformViewModel.HideTransform();
+        onEnded!.Invoke(this);
+    }
+
+    public override void ForceStop()
+    {
+        internals!.ActionAccumulator.AddFinishedActions(new EndTransformReferenceLayer_Action());
+        document!.TransformViewModel.HideTransform();
+    }
+}

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedAreaExecutor.cs

@@ -32,7 +32,7 @@ internal class TransformSelectedAreaExecutor : UpdateableChangeExecutor
             return ExecutionState.Error;
 
         ShapeCorners corners = new(document.SelectionPathBindable.TightBounds);
-        document.TransformViewModel.ShowTransform(DocumentTransformMode.Freeform, true, corners);
+        document.TransformViewModel.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_Perspective, true, corners);
         membersToTransform = members.Select(static a => a.GuidValue).ToArray();
         internals!.ActionAccumulator.AddActions(
             new TransformSelectedArea_Action(membersToTransform, corners, tool.KeepOriginalImage, false));

+ 4 - 3
src/PixiEditor/Models/Enums/DocumentTransformMode.cs

@@ -1,7 +1,8 @@
 namespace PixiEditor.Models.Enums;
 internal enum DocumentTransformMode
 {
-    NoRotation,
-    Rotation,
-    Freeform
+    Scale_NoRotate_NoShear_NoPerspective,
+    Scale_Rotate_NoShear_NoPerspective,
+    Scale_Rotate_Shear_NoPerspective,
+    Scale_Rotate_Shear_Perspective
 }

+ 23 - 7
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentTransformViewModel.cs

@@ -80,7 +80,7 @@ internal class DocumentTransformViewModel : NotifyableObject
 
     public event EventHandler<ShapeCorners>? TransformMoved;
 
-    private DocumentTransformMode activeTransformMode = DocumentTransformMode.Rotation;
+    private DocumentTransformMode activeTransformMode = DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
 
     public void HideTransform()
     {
@@ -92,7 +92,7 @@ internal class DocumentTransformViewModel : NotifyableObject
         activeTransformMode = mode;
         CornerFreedom = TransformCornerFreedom.Scale;
         SideFreedom = TransformSideFreedom.Stretch;
-        LockRotation = mode == DocumentTransformMode.NoRotation;
+        LockRotation = mode == DocumentTransformMode.Scale_NoRotate_NoShear_NoPerspective;
         RequestedCorners = initPos;
         CoverWholeScreen = coverWholeScreen;
         TransformActive = true;
@@ -124,10 +124,26 @@ internal class DocumentTransformViewModel : NotifyableObject
             requestedSideFreedom = TransformSideFreedom.Stretch;
         }
 
-        if (requestedCornerFreedom != TransformCornerFreedom.Free || activeTransformMode == DocumentTransformMode.Freeform)
-            CornerFreedom = requestedCornerFreedom;
-        if (requestedSideFreedom is not (TransformSideFreedom.Free or TransformSideFreedom.Shear) ||
-            activeTransformMode == DocumentTransformMode.Freeform)
-            SideFreedom = requestedSideFreedom;
+        switch (activeTransformMode)
+        {
+            case DocumentTransformMode.Scale_Rotate_Shear_Perspective:
+                CornerFreedom = requestedCornerFreedom;
+                SideFreedom = requestedSideFreedom;
+                break;
+
+            case DocumentTransformMode.Scale_Rotate_Shear_NoPerspective:
+                if (requestedCornerFreedom != TransformCornerFreedom.Free)
+                    CornerFreedom = requestedCornerFreedom;
+                SideFreedom = requestedSideFreedom;
+                break;
+
+            case DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective:
+            case DocumentTransformMode.Scale_NoRotate_NoShear_NoPerspective:
+                if (requestedCornerFreedom != TransformCornerFreedom.Free)
+                    CornerFreedom = requestedCornerFreedom;
+                if (requestedSideFreedom is not (TransformSideFreedom.Free or TransformSideFreedom.Shear))
+                    SideFreedom = requestedSideFreedom;
+                break;
+        }
     }
 }

+ 30 - 14
src/PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -310,13 +310,21 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
 
         VecI size = new VecI(bitmap.PixelWidth, bitmap.PixelHeight);
 
-        RectD referenceImageRect = new RectD(VecD.Zero, doc.SizeBindable).AspectFit(new RectD(VecD.Zero, size));
-        ShapeCorners corners = new ShapeCorners(referenceImageRect);
-
         doc.Operations.ImportReferenceLayer(
             pixels.ToImmutableArray(), 
-            size, 
-            corners);
+            size);
+    }
+    private string OpenReferenceLayerFilePicker()
+    {
+        var imagesFilter = new FileTypeDialogDataSet(FileTypeDialogDataSet.SetKind.Images).GetFormattedTypes();
+        OpenFileDialog dialog = new OpenFileDialog
+        {
+            Title = "Reference layer path",
+            CheckPathExists = true,
+            Filter = imagesFilter
+        };
+
+        return (bool)dialog.ShowDialog() ? dialog.FileName : null;
     }
 
     [Command.Basic("PixiEditor.Layer.DeleteReferenceLayer", "Delete reference layer", "Delete reference layer", "PixiEditor.Layer.ReferenceLayerExists")]
@@ -329,16 +337,24 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         doc.Operations.DeleteReferenceLayer();
     }
 
-    private string OpenReferenceLayerFilePicker()
+    [Command.Basic("PixiEditor.Layer.TransformReferenceLayer", "Transform reference layer", "Transform reference layer", "PixiEditor.Layer.ReferenceLayerExists")]
+    public void TransformReferenceLayer()
     {
-        var imagesFilter = new FileTypeDialogDataSet(FileTypeDialogDataSet.SetKind.Images).GetFormattedTypes();
-        OpenFileDialog dialog = new OpenFileDialog
-        {
-            Title = "Reference layer path",
-            CheckPathExists = true,
-            Filter = imagesFilter
-        };
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc is null)
+            return;
 
-        return (bool)dialog.ShowDialog() ? dialog.FileName : null;
+        doc.Operations.TransformReferenceLayer();
     }
+
+    [Command.Basic("PixiEditor.Layer.ResetReferenceLayerPosition", "Reset reference layer position", "Reset reference layer position", "PixiEditor.Layer.ReferenceLayerExists")]
+    public void ResetReferenceLayerPosition()
+    {
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc is null)
+            return;
+
+        doc.Operations.ResetReferenceLayerPosition();
+    }
+
 }

+ 26 - 2
src/PixiEditor/Views/UserControls/Layers/ReferenceLayer.xaml

@@ -42,7 +42,30 @@
                         IsThreeState="False" HorizontalAlignment="Center" 
                         IsChecked="{Binding Path=Document.ReferenceLayerViewModel.IsVisibleBindable, Mode=TwoWay, ElementName=uc}"/>
                 </Grid>
-                <StackPanel Name="middleStackPanel" Height="40" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Center">
+                <StackPanel Name="middleStackPanel" Height="40" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Left">
+                    <Button Cursor="Hand" Grid.Column="1"
+                            Command="{cmds:Command PixiEditor.Layer.ResetReferenceLayerPosition}"
+                            Visibility="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, ElementName=uc, Converter={BoolToVisibilityConverter}}" 
+                            Style="{StaticResource ImageButtonStyle}" 
+                            ToolTip="Reset reference layer position"
+                            RenderOptions.BitmapScalingMode="HighQuality"
+                            Margin="3"
+                            Width="20" Height="20" HorizontalAlignment="Right">
+                        <Button.Background>
+                            <ImageBrush ImageSource="/Images/Layout.png"/>
+                        </Button.Background>
+                    </Button>
+                    <Button Cursor="Hand" Grid.Column="1"
+                            Command="{cmds:Command PixiEditor.Layer.TransformReferenceLayer}"
+                            Visibility="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, ElementName=uc, Converter={BoolToVisibilityConverter}}" 
+                            Style="{StaticResource ImageButtonStyle}" 
+                            ToolTip="Transform reference layer"
+                            RenderOptions.BitmapScalingMode="HighQuality"
+                            Width="25" Height="25" HorizontalAlignment="Right">
+                        <Button.Background>
+                            <ImageBrush ImageSource="/Images/Tools/MoveImage.png"/>
+                        </Button.Background>
+                    </Button>
                     <Border HorizontalAlignment="Left" 
                             Visibility="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, ElementName=uc, Converter={converters:NotNullToVisibilityConverter}}" 
                             Width="30" Height="30"
@@ -52,7 +75,7 @@
                         <Image Source="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap,ElementName=uc}" Stretch="Uniform" Width="26" Height="26"
                            RenderOptions.BitmapScalingMode="HighQuality" IsHitTestVisible="False"/>
                     </Border>
-                    <Image Margin="0 0 5 0" Width="20" Source="/Images/Layer-add.png"  
+                    <Image Margin="30 0 5 0" Width="20" Source="/Images/Layer-add.png"  
                            Visibility="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, ElementName=uc, Converter={converters:NullToVisibilityConverter}}"/>
 
                     <local1:PrependTextBlock IsEnabled="{Binding ElementName=uc, Path=IsEnabled}" 
@@ -63,6 +86,7 @@
                             Command="{cmds:Command PixiEditor.Layer.DeleteReferenceLayer}"
                             Visibility="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, ElementName=uc, Converter={BoolToVisibilityConverter}}" 
                             Style="{StaticResource ImageButtonStyle}" 
+                            RenderOptions.BitmapScalingMode="HighQuality"
                             Width="20" Height="20" HorizontalAlignment="Right">
                         <Button.Background>
                             <ImageBrush ImageSource="/Images/Trash.png"/>

+ 16 - 3
src/PixiEditor/Views/UserControls/TransformOverlay/TransformUpdateHelper.cs

@@ -45,9 +45,22 @@ internal static class TransformUpdateHelper
 
             // find by how much move each corner
             VecD delta = (desiredPos - targetPos).Rotate(-angle);
-            VecD leftNeighDelta = TransferZeros(leftNeighTrans, delta);
-            VecD rightNeighDelta = TransferZeros(rightNeighTrans, delta);
-            
+            VecD leftNeighDelta, rightNeighDelta;
+            if (corners.IsPartiallyDegenerate)
+            {
+                // handle cases where we'd need to scale by infinity
+                leftNeighDelta = TransferZeros(leftNeighTrans, delta);
+                rightNeighDelta = TransferZeros(rightNeighTrans, delta);
+            }
+            else
+            {
+                // handle normal cases
+                VecD desiredTrans = (desiredPos - oppositePos).Rotate(-angle);
+                VecD scaling = desiredTrans.Divide(targetTrans);
+                leftNeighDelta = leftNeighTrans.Multiply(scaling) - leftNeighTrans;
+                rightNeighDelta = rightNeighTrans.Multiply(scaling) - rightNeighTrans;
+            }
+
             // handle cases where the transform overlay is squished into a line or a single point
             bool squishedWithLeft = leftNeighTrans.TaxicabLength < epsilon;
             bool squishedWithRight = rightNeighTrans.TaxicabLength < epsilon;