public void Calculate_does_not_produce_textures_that_overlap_2()
        {
            var WIDTH = 512;
            var HEIGHT = 512;
            var input = new TextureAtlasInput(GetSettings(WIDTH, HEIGHT, 1));
            var calc = new CornersPacker();

            AddTexture(input, 100, 50, "a");
            AddTexture(input, 80, 80, "c1");
            AddTexture(input, 100, 50, "b");
            AddTexture(input, 80, 80, "c3");
            AddTexture(input, 80, 80, "c4");
            AddTexture(input, 80, 80, "c2");
            for (int i = 0; i < 40; i++) AddTexture(input, 20, 20, "a" + i);
            for (int i = 0; i < 10; i++) AddTexture(input, 80 + 10 * i, 40, "b" + i);
            var result = calc.Calculate(input);

            var nodes = result.Nodes.ToList();
            var failures = new List<TextureAtlasNode>();

            for (int i = 0; i < nodes.Count(); i++)
                for (int j = i + 1; j < nodes.Count(); j++)
                {
                    if (nodes[i].GetBounds().IntersectsWith(nodes[j].GetBounds()))
                    {
                        failures.Add(nodes[i]);
                        j = nodes.Count;
                    }
                }

            Assert.Equal(0, failures.Count);

            Render(result);
        }
        public TextureAtlas Calculate(TextureAtlasInput input)
        {
            ValidateInput(input);

            var unplacedNodes = input.Nodes.Select(n => n).ToList();
            var placedNodes = new List<TextureAtlasNode>();

            while (unplacedNodes.Any())
            {
                var validPlacements = unplacedNodes
                    .SelectMany(n => GetScoredPlacements(n, placedNodes, input.Settings))
                    .Where(n => n.Score.IsVaildPlacement)
                    .ToList();

                var best = validPlacements
                    .OrderBy(n => n.Score.CornerParity)
                    .ThenBy(n => n.Score.WastageScore)
                    .ThenByDescending(n => n.Score.UtilizationScore)
                    .FirstOrDefault();

                if (best == null) throw new InvalidDataException("Insufficient free space available after " + placedNodes.Count + " textures placed");

                best.PlaceNode();
                placedNodes.Add(best.SourceNode);
                unplacedNodes.Remove(best.SourceNode);

                // TODO: Raise event node placed
            }

            return new TextureAtlas
            {
                Nodes = placedNodes,
                Size = input.Settings.Size,
            };
        }
 public void Add_throws_when_any_input_image_exceeds_output_size()
 {
     var settings = GetCalculatorSettings(100, 100, 0);
     var input = new TextureAtlasInput(settings);
     Assert.Throws<ArgumentOutOfRangeException>(() =>
         input.AddSprite(GetBitmap(256, 10, "Invalid"), "Invalid")
         );
 }
        private static void ValidateInput(TextureAtlasInput input)
        {
            if (!input.Nodes.Any()) throw new InvalidOperationException("No input textures provided");

            var totalInputArea = input.Nodes.Sum(x => x.Size.Pad(input.Settings.Padding).Area());
            var totalOutputArea = input.Settings.Size.Pad(-input.Settings.Padding).Area();
            if (totalInputArea > totalOutputArea) throw new ArgumentException("Input images (and padding) exceed the maximum total output area");
        }
        public void Calculate_does_not_produce_textures_outside_atlas_bounds()
        {
            var WIDTH = 512;
            var HEIGHT = 512;
            var input = new TextureAtlasInput(GetSettings(WIDTH, HEIGHT,1));
            var calc = new CornersPacker();

            for (int i = 0; i < 40; i++) AddTexture(input, 20, 20, "a" + i);
            for (int i = 0; i < 10; i++) AddTexture(input, 80 + 10 * i, 40, "b" + i);
            for (int i = 0; i < 40; i++) AddTexture(input, 40 + i, 10, "c" + i);

            var result = calc.Calculate(input);

            var failures = new List<TextureAtlasNode>();
            result.Nodes.ToList().ForEach(n =>
            {
                if (n.X < 0 || n.Y < 0 || n.X > WIDTH - n.Texture.Width || n.Y > HEIGHT - n.Texture.Height)
                    failures.Add(n);
            });
            Assert.Equal(0, failures.Count);

            Render(result);
        }
 private void AddTexture(TextureAtlasInput input, int width, int height, string reference)
 {
     input.AddSprite(GetBitmap(width,height,reference),reference);
 }
 public void Calculate_throws_when_no_images_added()
 {
     var input = new TextureAtlasInput(GetSettings());
     var calc = new CornersPacker();
     Assert.Throws<InvalidOperationException>(() => calc.Calculate(input));
 }
 public void Calculate_throws_when_insufficient_output_size_to_fit_all_images_with_padding()
 {
     var input = new TextureAtlasInput(GetSettings(100, 100, 1));
     var calc = new CornersPacker();
     AddTexture(input, 50, 50, "a");
     AddTexture(input, 50, 50, "b");
     AddTexture(input, 50, 50, "c");
     AddTexture(input, 50, 50, "d");
     Assert.Throws<ArgumentException>(() => calc.Calculate(input));
 }
        public void Calculate_returns_all_added_references()
        {
            var input = new TextureAtlasInput(GetSettings(100, 100, 1));
            var calc = new CornersPacker();

            AddTexture(input, 10, 10, "a");
            AddTexture(input, 10, 10, "b");
            AddTexture(input, 10, 10, "c");

            var result = calc.Calculate(input);

            Assert.NotNull(result.Nodes.SingleOrDefault(n => n.Reference == "a"));
            Assert.NotNull(result.Nodes.SingleOrDefault(n => n.Reference == "b"));
            Assert.NotNull(result.Nodes.SingleOrDefault(n => n.Reference == "c"));

            Render(result);
        }
        public void Calculate_non_test()
        {
            var WIDTH = 512;
            var HEIGHT = 512;
            var input = new TextureAtlasInput(GetSettings(WIDTH, HEIGHT, 1));
            var calc = new CornersPacker();

            for (int i = 0; i < 40; i++) AddTexture(input, 20, 20, "a" + i);
            for (int i = 0; i < 10; i++) AddTexture(input, 80 + 10 * i, 40, "b" + i);
            for (int i = 0; i < 40; i++) AddTexture(input, 40 + i, 10, "c" + i);
            AddTexture(input, 300, 40, "a");
            AddTexture(input, 50, 400, "d");
            AddTexture(input, 80, 80, "d2");
            for (int i = 0; i < 40; i++) AddTexture(input, 20, 20, "e" + i);
            for (int i = 0; i < 10; i++) AddTexture(input, 80 + 10 * i, 40, "f" + i);
            var result = calc.Calculate(input);

            Render(result);
        }
 public void Calculate_does_not_throw_when_images_fit_within_output()
 {
     var input = new TextureAtlasInput(GetSettings());
     var calc = new CornersPacker();
     AddTexture(input, 50, 50, "a");
     AddTexture(input, 50, 50, "b");
     AddTexture(input, 50, 50, "c");
     AddTexture(input, 50, 50, "d");
     calc.Calculate(input);
 }
 public void Add_throws_on_null_reference()
 {
     var settings = GetCalculatorSettings(100, 100, 0);
     var input = new TextureAtlasInput(settings);
     Assert.Throws<ArgumentNullException>(() => input.AddSprite(GetBitmap(10, 10, "a"), null));
 }
 public void Add_throws_on_null_image()
 {
     var settings = GetCalculatorSettings(100, 100, 0);
     var input = new TextureAtlasInput(settings);
     Assert.Throws<ArgumentNullException>(() => input.AddSprite(null, "invalid"));
 }
 public void Add_throws_on_empty_reference(string reference)
 {
     var settings = GetCalculatorSettings(100, 100, 0);
     var input = new TextureAtlasInput(settings);
     Assert.Throws<ArgumentOutOfRangeException>(() => input.AddSprite(GetBitmap(10, 10, "a"), reference));
 }