private IEnumerable<PlacementPosition> GetPossibleNodePlacementsForCorner(Point corner, TextureAtlasNode node, List<TextureAtlasNode> placedNodes, CalculatorSettings settings)
        {
            var boundaryArea = settings.Size.ToRectangle();

            var possiblePlacementRects =  new[] {
                new Rectangle(corner.X, corner.Y, node.Size.Width, node.Size.Height),
                new Rectangle(corner.X, corner.Y, -node.Size.Width, node.Size.Height).Normalize(),
                new Rectangle(corner.X, corner.Y, -node.Size.Width, -node.Size.Height).Normalize(),
                new Rectangle(corner.X, corner.Y, node.Size.Width, -node.Size.Height).Normalize()
            };

            var validPlacements = possiblePlacementRects
                .Where(r => r.IsEntirelyContainedBy(boundaryArea)).ToArray();

            var xx = validPlacements.Where(r => !placedNodes.Any(n => n.GetBounds().IntersectsWith(r))).ToArray();

            foreach(var rect in xx)
            {
                var result = new PlacementPosition(rect.X, rect.Y, false, rect.Width, rect.Height);
                yield return result;
            }

            // No point rotating a square!
            if (!settings.IsRotationEnabled || node.Size.Width == node.Size.Height) yield break;

            foreach (var rect in new[] {
                new Rectangle(corner.X, corner.Y, node.Size.Height, node.Size.Width),
                new Rectangle(corner.X, corner.Y, -node.Size.Height, node.Size.Width).Normalize(),
                new Rectangle(corner.X, corner.Y, -node.Size.Height, -node.Size.Width).Normalize(),
                new Rectangle(corner.X, corner.Y, node.Size.Height, -node.Size.Width).Normalize(),
            }
                .Where(r => r.IsEntirelyContainedBy(boundaryArea))
                .Where(r => placedNodes.Any(n => n.GetBounds().IntersectsWith(r)))
                )
            {
                var result = new PlacementPosition(rect.X, rect.Y, true, rect.Width, rect.Height);
                yield return result;
            }
        }
        private PlacementScore Score(Point corner, PlacementPosition placement, List<TextureAtlasNode> placedNodes, Size outputTextureSize)
        {
            var result = new PlacementScore();

            var edges = GetEdges(outputTextureSize, placedNodes).ToList();
            var corners = GetCorners(outputTextureSize, placedNodes).ToList();

            var sharedEdgeSum = placement
                .GetBounds()
                .GetEdges()
                .Sum(e => edges.Sum(placedEdge => e.GetOverlap(placedEdge)));

            var cornerDistance = placement
                .GetBounds()
                .GetCorners()
                .Sum(p => corners.Min(c => p.DistanceBetween(c)));

            result.UtilizationScore = sharedEdgeSum;
            result.WastageScore = placement.GetBounds().GetEdges().Sum(e=>e.Length) - sharedEdgeSum;

            result.CornerParity = cornerDistance;

            result.IsVaildPlacement = true;

            return result;
        }