C# 二叉树 2D 打包算法旋转

问题描述

我正在使用 Binary-Tree-2D-Packing 这个算法在容器中打包矩形。它工作正常,但如果它不适合根节点的右侧,我想旋转该节点。它应该在正确的节点上尝试宽度和高度,如果它不合适,它应该旋转它并使宽度 = 高度和高度 = 宽度然后再试一次。我怎样才能做到这一点?

public class Packer
        {
            public Packer()
            {
                Boxes = new List<Box>();
            }
    public class Node
    {
        public Node right;
        public Node down;
        public double x;
        public double y;
        public double w;
        public double h;
        public bool used;
    }

    public class Box
    {
        public double height;
        public double width;
        public double area;
        public Node position;
    }

    public List<Box> Boxes;
    private Node rootNode;

    public void AddBox(Box Box)
    {
        Box.area = Box.width * Box.height;
        Boxes.Add(Box);
    }

    public void Pack(double containerWidth,double containerHeight)
    {
        Boxes = Boxes.OrderByDescending(x => x.area).ToList();
        rootNode = new Node { w = containerWidth,h = containerHeight };

        foreach (var Box in Boxes)
        {
            var node = FindNode(rootNode,Box.width,Box.height);
            if (node != null)
            {
                Box.position = SplitNode(node,Box.height);
            }
            else
            {
                Box.position = GrowNode(Box.width,Box.height);
            }
        }
    }

    private Node FindNode(Node rootNode,double w,double h)
    {
        if (rootNode.used)
        {
            var nextNode = FindNode(rootNode.right,w,h);

            if (nextNode == null)
            {
                nextNode = FindNode(rootNode.down,h);
            }

            return nextNode;
        }
        else if (w <= rootNode.w && h <= rootNode.h)
        {
            return rootNode;
        }
        else
        {
            return null;
        }
    }

    private Node SplitNode(Node node,double h)
    {
        
        node.used = true;
        node.down = new Node { x = node.x,y = node.y + h,w = node.w,h = node.h - h };
        node.right = new Node { x = node.x + w,y = node.y,w = node.w - w,h = h };
        return node;
    }

    private Node GrowNode(double w,double h)
    {
        bool cangrowDown = (w <= rootNode.w);
        bool cangrowRight = (h <= rootNode.h);

        bool shouldGrowRight = cangrowRight && (rootNode.h >= (rootNode.w + w));
        bool shouldGrowDown = cangrowDown && (rootNode.w >= (rootNode.h + h));

        if (shouldGrowRight)
        {

            return growRight(w,h);
        }
        else if (shouldGrowDown)
        {

            return growDown(w,h);
        }
        else if (cangrowRight)
        {

            return growRight(w,h);
        }
        else if (cangrowDown)
        {

            return growDown(w,h);
        }
        else
        {

            return null;
        }
    }

    private Node growRight(double w,double h)
    {
        rootNode = new Node()
        {
            used = true,x = 0,y = 0,w = rootNode.w + w,h = rootNode.h,down = rootNode,right = new Node() { x = rootNode.w,w = w,h = rootNode.h }
        };

        Node node = FindNode(rootNode,h);
        if (node != null)
        {
            return SplitNode(node,h);
        }
        else
        {
            return null;
        }
    }

    private Node growDown(double w,w = rootNode.w,h = rootNode.h + h,down = new Node() { x = 0,y = rootNode.h,h = h },right = rootNode
        };
        Node node = FindNode(rootNode,h);
        }
        else
        {
            return null;
        }
    }
}

解决方法

我将旋转变量添加到 Node

public class Node
    {
        public Node right;
        public Node down;
        public double x;
        public double y;
        public double w;
        public double h;
        public bool used;
        public bool rotated;
    }

并在 FindNode 方法中添加了旋转条件

private Node FindNode(Node rootNode,double w,double h)
    {
        if (rootNode.used)
        {

            var nextNode = FindNode(rootNode.right,w,h);

            if (nextNode == null)
            {

                nextNode = FindNode(rootNode.right,h,w);
                if (nextNode!=null) { nextNode.rotated = true; }
                
                if (nextNode == null)
                {
                    nextNode = FindNode(rootNode.down,h);
                }

            }

            return nextNode;
        }
        else if (w <= rootNode.w && h <= rootNode.h)
        {
            return rootNode;
        }
        else
        {
            return null;
        }
    }

最后检查旋转变量是否为真,

public void Pack(double containerWidth,double containerHeight)
    {
        boxes = boxes.OrderByDescending(x => x.area).ToList();
        rootNode = new Node { w = containerWidth,h = containerHeight };

        foreach (var box in boxes)
        {
            var node = FindNode(rootNode,box.width,box.height);
            if (node != null)
            {

            
            if (node.rotated)
            {
                double tmp = 0;
                tmp = box.width;
                box.width = box.height;
                box.height = tmp;
            }
            }
            if (node != null)
            {
                box.position = SplitNode(node,box.height);
            }
            else
            {
                box.position = GrowNode(box.width,box.height);
            }
        }
    }
,

除非我误解了某些东西,否则它不会只是两个测试而不是一个吗?即

        var node = FindNode(rootNode,box.height);
        if (node != null)
        {
            box.position = SplitNode(node,box.height);
        }
        else
        {
            node = FindNode(rootNode,box.height,box.width);
            if(node!= null){
                  box.position = SplitNode(node,box.width);
            }

            box.position = GrowNode(box.width,box.height);
        }

请注意,GrowNode 将始终按宽高顺序增长。

如果树是不可变的,您也可以尝试每个替代方案并比较结果节点。:

       Node whNode,hwNode;

       var node = FindNode(rootNode,box.height);           
        if (node != null)
        {
            whNode = SplitNode(node,box.height);
        }
        else
        {
            whNode = GrowNode(box.width,box.height);
        }

        node = FindNode(rootNode,box.width);
        if (node != null)
        {
            hwNode= SplitNode(node,box.width);
        }
        else
        {
            hwNode= GrowNode(box.height,box.width);
        }
        box.Position = GetBestNode(whNode,hwNode);