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;
            }
        }
 public PlacementNode(TextureAtlasNode sourceNode)
 {
     SourceNode = sourceNode;
     Score = new PlacementScore();
     Placement = new PlacementPosition(0, 0, false, 0, 0);
 }
        /// <summary>
        /// Find the best possible place for the current node and gibe 
        /// </summary>        
        private IEnumerable<PlacementNode> GetScoredPlacements(TextureAtlasNode node, List<TextureAtlasNode> placedNodes, CalculatorSettings settings)
        {
            var result = new PlacementNode(node);

            //// If this is the first node to be placed, just put it in a corner
            //// TODO: which is better - long size on long size or long on short side?
            ////    Look for minimum waste placement
            //if (!placedNodes.Any())
            //{
            //    result.Score.IsVaildPlacement = true;
            //    result.Score.UtilizationScore = node.Size.Width + node.Size.Height;
            //    result.Score.WastageScore = 0;
            //    yield return result;
            //    yield break;
            //}

            var usedSpace = placedNodes.Select(n=>n.GetBounds()).ToArray();
            var availableCorners = GetCorners(settings.Size, placedNodes)
                .Distinct()
                .Where(c => !c.IsSurroundedBy(usedSpace));

            foreach (var corner in availableCorners)
            {
                foreach (var placement in GetPossibleNodePlacementsForCorner(corner, node, placedNodes, settings))
                {
                    var score = Score(corner, placement, placedNodes, settings.Size);
                    if (score.IsVaildPlacement)
                    {
                        yield return new PlacementNode(node) {
                            Score = score,
                            Placement = placement,
                        };
                    }
                }
            }
        }