public TextureAtlasInput(CalculatorSettings settings)
        {
            ValidateSettings(settings);

            Settings = settings;
            _nodes = new Dictionary<string, TextureAtlasNode>();
        }
 private void ValidateSettings(CalculatorSettings settings)
 {
     if (settings.Size.Height <= 0) throw new ArgumentOutOfRangeException("Size.Height", settings.Size.Height, "Output texture height cannot be 0 or less");
     if (settings.Size.Width <= 0) throw new ArgumentOutOfRangeException("Size.Width", settings.Size.Width, "Output texture width cannot be 0 or less");
     if (settings.Padding < 0) throw new ArgumentOutOfRangeException("Padding", settings.Padding, "Output texture padding cannot be less than 0");
     if ((settings.Padding * 2 >= settings.Size.Height) || (settings.Padding * 2 >= settings.Size.Width))
         throw new ArgumentOutOfRangeException("Padding", settings.Padding, "Padding cannot be more than half of the output texture height and/or width");
 }
        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;
            }
        }
        /// <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,
                        };
                    }
                }
            }
        }
 public Settings()
 {
     CalculatorSettings = new CalculatorSettings();
     RendererSettings = new TextureAtlasRendererSettings();
 }