using EnvelopeRenderer.Desktop.Core.Design; namespace EnvelopeRenderer.Desktop.Tests; public class AlignmentCalculatorTests { // Three boxes at varying positions/sizes, reused across several tests: // A: (0,0)-(10,10) B: (50,20)-(70,30) C: (100,-10)-(110,0) private static readonly (double MinX, double MinY, double MaxX, double MaxY)[] ThreeBoxes = { (0, 0, 10, 10), (50, 20, 70, 30), (100, -10, 110, 0), }; [Fact] public void ComputeAlignmentDeltas_Left_MovesEveryBoxToTheLeftmostMinX() { var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.Left); Assert.Equal(0.0, deltas[0].Dx, precision: 6); // A is already leftmost (MinX=0) Assert.Equal(-50.0, deltas[1].Dx, precision: 6); // B's MinX=50 -> 0 Assert.Equal(-100.0, deltas[2].Dx, precision: 6); // C's MinX=100 -> 0 Assert.All(deltas, d => Assert.Equal(0.0, d.Dy, precision: 6)); } [Fact] public void ComputeAlignmentDeltas_Right_MovesEveryBoxToTheRightmostMaxX() { var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.Right); // Rightmost MaxX across all three is 110 (box C). Assert.Equal(100.0, deltas[0].Dx, precision: 6); // A's MaxX=10 -> 110 Assert.Equal(40.0, deltas[1].Dx, precision: 6); // B's MaxX=70 -> 110 Assert.Equal(0.0, deltas[2].Dx, precision: 6); // C already rightmost } [Fact] public void ComputeAlignmentDeltas_Top_MovesEveryBoxToTheTopmostMaxY() { var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.Top); // Topmost MaxY across all three is 30 (box B). Assert.Equal(20.0, deltas[0].Dy, precision: 6); // A's MaxY=10 -> 30 Assert.Equal(0.0, deltas[1].Dy, precision: 6); // B already topmost Assert.Equal(30.0, deltas[2].Dy, precision: 6); // C's MaxY=0 -> 30 Assert.All(deltas, d => Assert.Equal(0.0, d.Dx, precision: 6)); } [Fact] public void ComputeAlignmentDeltas_Bottom_MovesEveryBoxToTheBottommostMinY() { var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.Bottom); // Bottommost MinY across all three is -10 (box C). Assert.Equal(-10.0, deltas[0].Dy, precision: 6); // A's MinY=0 -> -10 Assert.Equal(-30.0, deltas[1].Dy, precision: 6); // B's MinY=20 -> -10 Assert.Equal(0.0, deltas[2].Dy, precision: 6); // C already bottommost } [Fact] public void ComputeAlignmentDeltas_HorizontalCenter_MovesEveryBoxToTheOverallHorizontalCenter() { // Overall bounds: MinX=0 (A), MaxX=110 (C) -> horizontal center = 55. var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.HorizontalCenter); Assert.Equal(50.0, deltas[0].Dx, precision: 6); // A's center=5 -> 55 Assert.Equal(-5.0, deltas[1].Dx, precision: 6); // B's center=60 -> 55 Assert.Equal(-50.0, deltas[2].Dx, precision: 6); // C's center=105 -> 55 } [Fact] public void ComputeAlignmentDeltas_VerticalCenter_MovesEveryBoxToTheOverallVerticalCenter() { // Overall bounds: MinY=-10 (C), MaxY=30 (B) -> vertical center = 10. var deltas = AlignmentCalculator.ComputeAlignmentDeltas(ThreeBoxes, AlignmentCalculator.Edge.VerticalCenter); Assert.Equal(5.0, deltas[0].Dy, precision: 6); // A's center=5 -> 10 Assert.Equal(-15.0, deltas[1].Dy, precision: 6); // B's center=25 -> 10 Assert.Equal(15.0, deltas[2].Dy, precision: 6); // C's center=-5 -> 10 } [Fact] public void ComputeAlignmentDeltas_EmptyList_ReturnsEmpty() { var deltas = AlignmentCalculator.ComputeAlignmentDeltas(Array.Empty<(double, double, double, double)>(), AlignmentCalculator.Edge.Left); Assert.Empty(deltas); } [Fact] public void ComputeDistributionDeltas_ThreeItems_KeepsExtremesFixedAndCentersTheMiddleOne() { // Centers along X: A=5, B=60, C=105. Ordered: A(5), B(60), C(105). // Evenly spaced target centers between the extremes (5 and 105): 5, 55, 105. var deltas = AlignmentCalculator.ComputeDistributionDeltas(ThreeBoxes, AlignmentCalculator.Axis.Horizontal); Assert.Equal(0.0, deltas[0].Dx, precision: 6); // A (leftmost extreme) stays fixed Assert.Equal(-5.0, deltas[1].Dx, precision: 6); // B's center 60 -> 55 Assert.Equal(0.0, deltas[2].Dx, precision: 6); // C (rightmost extreme) stays fixed Assert.All(deltas, d => Assert.Equal(0.0, d.Dy, precision: 6)); } [Fact] public void ComputeDistributionDeltas_Vertical_UsesYCentersInstead() { // Centers along Y: A=5, B=25, C=-5. Ordered by Y center: C(-5), A(5), B(25). // Evenly spaced target centers between the extremes (-5 and 25): -5, 10, 25. var deltas = AlignmentCalculator.ComputeDistributionDeltas(ThreeBoxes, AlignmentCalculator.Axis.Vertical); Assert.Equal(5.0, deltas[0].Dy, precision: 6); // A's center 5 -> 10 (middle rank) Assert.Equal(0.0, deltas[1].Dy, precision: 6); // B (topmost extreme) stays fixed Assert.Equal(0.0, deltas[2].Dy, precision: 6); // C (bottommost extreme) stays fixed } [Fact] public void ComputeDistributionDeltas_FewerThanThreeItems_ReturnsAllZeroDeltas() { var twoBoxes = new[] { ThreeBoxes[0], ThreeBoxes[2] }; var deltas = AlignmentCalculator.ComputeDistributionDeltas(twoBoxes, AlignmentCalculator.Axis.Horizontal); Assert.All(deltas, d => Assert.Equal((0.0, 0.0), d)); } [Fact] public void ComputeDistributionDeltas_EmptyList_ReturnsEmpty() { var deltas = AlignmentCalculator.ComputeDistributionDeltas(Array.Empty<(double, double, double, double)>(), AlignmentCalculator.Axis.Horizontal); Assert.Empty(deltas); } [Fact] public void ComputeDistributionDeltas_FourItems_SpacesTheTwoMiddleOnesEvenly() { var boxes = new (double MinX, double MinY, double MaxX, double MaxY)[] { (0, 0, 2, 2), // center X = 1 (10, 0, 12, 2), // center X = 11 (out of order on purpose) (30, 0, 32, 2), // center X = 31 (rightmost extreme) (5, 0, 7, 2), // center X = 6 }; var deltas = AlignmentCalculator.ComputeDistributionDeltas(boxes, AlignmentCalculator.Axis.Horizontal); // Order by center: box0(1), box3(6), box1(11), box2(31). Step = (31-1)/3 = 10. // Target centers by rank: 1, 11, 21, 31. Assert.Equal(0.0, deltas[0].Dx, precision: 6); // box0 stays at center 1 Assert.Equal(10.0, deltas[1].Dx, precision: 6); // box1's center 11 -> 21 Assert.Equal(0.0, deltas[2].Dx, precision: 6); // box2 (rightmost extreme) stays fixed Assert.Equal(5.0, deltas[3].Dx, precision: 6); // box3's center 6 -> 11 } }