Javascript 箱子包装Js实现,使用箱子旋转实现最佳匹配

Javascript 箱子包装Js实现,使用箱子旋转实现最佳匹配,javascript,html,algorithm,sorting,bin-packing,Javascript,Html,Algorithm,Sorting,Bin Packing,我在这里使用了bin packing js实现 当我将帧大小指定为800x600时 和块大小为150x700150x700它会说,它不能容纳然而,有足够的空间。同样,当700x150、700x150制造出来时,它将适合它 如何调整代码,使其能够动态旋转块大小并适应帧 这里使用的js封隔器是 Packer = function(w, h) { this.init(w, h); }; Packer.prototype = { init: function(w, h) {

我在这里使用了bin packing js实现

当我将帧大小指定为800x600时

和块大小为150x700150x700它会说,它不能容纳然而,有足够的空间。同样,当700x150、700x150制造出来时,它将适合它

如何调整代码,使其能够动态旋转块大小并适应帧

这里使用的js封隔器是

    Packer = function(w, h) {
  this.init(w, h);
};

Packer.prototype = {

  init: function(w, h) {
    this.root = { x: 0, y: 0, w: w, h: h };
  },

  fit: function(blocks) {
    var n, node, block;
    for (n = 0; n < blocks.length; n++) {
      block = blocks[n];
      if (node = this.findNode(this.root, block.w, block.h))
        block.fit = this.splitNode(node, block.w, block.h);
    }
  },

  findNode: function(root, w, h) {
    if (root.used)
      return this.findNode(root.right, w, h) || this.findNode(root.down, w, h);
    else if ((w <= root.w) && (h <= root.h))
      return root;
    else
      return null;
  },

  splitNode: function(node, w, h) {
    node.used = true;
    node.down  = { x: node.x,     y: node.y + h, w: node.w,     h: node.h - h };
    node.right = { x: node.x + w, y: node.y,     w: node.w - w, h: h          };
    return node;
  }

}
Packer=功能(w,h){
这是init(w,h);
};
Packer.prototype={
init:函数(w,h){
this.root={x:0,y:0,w:w,h:h};
},
拟合:功能(块){
变量n,节点,块;
对于(n=0;n否则,如果((w我认为下面的代码可能会起作用…?!(即,我做了有限的测试,但对于我测试的内容,它似乎有效。)

实际上,我在findnode例程中添加了另一个选项来旋转块(即,切换宽度和高度尺寸)作为一个选项,如果它不符合预定义的方向。这涉及到将另一个属性添加到,称为旋转,作为交换尺寸的指示器。(当然,交换和rotate属性的引入需要将block传递到findnode,而不是像前面的代码那样,传递wh。)

Packer=功能(w,h){
这是init(w,h);
};
Packer.prototype={
init:函数(w,h){
this.root={x:0,y:0,w:w,h:h};
},
拟合:功能(块){
变量n,节点,块;
对于(n=0;n}否则如果((block.wAm添加第二个答案,因为这与第一个答案完全不同,并尝试使用问题中提供的2D装箱算法解决更核心的问题。使用该特定算法,
splitNode
例程生成可用于拟合b的
down
right
节点锁定,但不考虑随着可用节点的累积,相邻节点的并集可以容纳更大的块的可能性

例如,在下面提出的算法中,给定一个800x600初始堆,放置一个500x300块将导致堆中包含两个堆块(0300)-(800600)和(500,0)-(800600)。这两个堆块将在(500300)-(800600)区域重叠以确保在搜索适合块的位置时显示最大堆块区域。而在二维装箱算法中,
down
right
中没有相交区域,而是有利于一个或另一个节点的潜在重叠空间

下面的算法试图通过实现一个表示最大可用块的可用堆块数组来弥补这一缺点,即使这些堆块彼此重叠。缺点是这引入了一个O(n^2)算法(
unionAll
)来管理堆,位于O(n)循环(
fit
)之上因此,该算法的性能可能接近O(n^3),尽管这可能是一种更糟糕的情况

Packer = function(w, h) {
  this.init(w, h);
};

Packer.prototype = {

  init: function(w, h) {
    this._root = { x: 0, y: 0, w: w, h: h }
  },

  intersect: function(block0, block1) {
    //
    // Returns the intersecting block of
    // block0 and block1.
    //
    let ix0 = Math.max(block0.x0, block1.x0);
    let ix1 = Math.min(block0.x1, block1.x1);
    let iy0 = Math.max(block0.y0, block1.y0);
    let iy1 = Math.min(block0.y1, block1.y1);

    if (ix0 <= ix1 && iy0 <= iy1) {
      return {x0: ix0, y0: iy0, x1: ix1, y1: iy1};
    } else {
      return null;
    }
  },

  chunkContains:  function(heapBlock0, heapBlock1) {
    //
    // Determine whether heapBlock0 totally encompasses (ie, contains) heapBlock1.
    //
    return heapBlock0.x0 <= heapBlock1.x0 && heapBlock0.y0 <= heapBlock1.y0 && heapBlock1.x1 <= heapBlock0.x1 && heapBlock1.y1 <= heapBlock0.y1;
  },

  expand: function(heapBlock0, heapBlock1) {
    //
    // Extend heapBlock0 and heapBlock1 if they are
    // adjoining or overlapping.
    //
    if (heapBlock0.x0 <= heapBlock1.x0 && heapBlock1.x1 <= heapBlock0.x1 && heapBlock1.y0 <= heapBlock0.y1) {
      heapBlock1.y0 = Math.min(heapBlock0.y0, heapBlock1.y0);
      heapBlock1.y1 = Math.max(heapBlock0.y1, heapBlock1.y1);
    }

    if (heapBlock0.y0 <= heapBlock1.y0 && heapBlock1.y1 <= heapBlock0.y1 && heapBlock1.x0 <= heapBlock0.x1) {
      heapBlock1.x0 = Math.min(heapBlock0.x0, heapBlock1.x0);
      heapBlock1.x1 = Math.max(heapBlock0.x1, heapBlock1.x1);
    }
  },

  unionMax: function(heapBlock0, heapBlock1) {
    //
    // Given two heap blocks, determine whether...
    //
    if (heapBlock0 && heapBlock1) {
      // ...heapBlock0 and heapBlock1 intersect, and if so...
      let i = this.intersect(heapBlock0, heapBlock1);
      if (i) {
        if (this.chunkContains(heapBlock0, heapBlock1)) {
          // ...if heapBlock1 is contained by heapBlock0...
          heapBlock1 = null;
        } else if (this.chunkContains(heapBlock1, heapBlock0)) {
          // ...or if heapBlock0 is contained by heapBlock1...
          heapBlock0 = null;
        } else {
          // ...otherwise, let's expand both heapBlock0 and
          // heapBlock1 to encompass as much of the intersected
          // space as possible.  In this instance, both heapBlock0
          // and heapBlock1 will overlap.
          this.expand(heapBlock0, heapBlock1);
          this.expand(heapBlock1, heapBlock0);
        }
      }
    }
  },

  unionAll: function() {
    //
    // Loop through the entire heap, looking to eliminate duplicative
    // heapBlocks, and to extend adjoining or intersecting heapBlocks,
    // despite this introducing overlapping heapBlocks.
    //
    for (let i = 0; i < this.heap.length; i++) {
      for (let j = 0; j < this.heap.length; j++) {
        if (i !== j) {
          this.unionMax(this.heap[i],this.heap[j]);
          if (this.heap[i] && this.heap[j]) {
            if (this.chunkContains(this.heap[j], this.heap[i])) {
              this.heap[i] = null;
            } else if (this.chunkContains(this.heap[i], this.heap[j])) {
              this.heap[j] = null;
            }
          }
        }
      }
    }
    // Eliminate the duplicative (ie, nulled) heapBlocks.
    let onlyBlocks = [];
    for (let i = 0; i < this.heap.length; i++) {
      if (this.heap[i]) {
        onlyBlocks.push(this.heap[i]);
      }
    }
    this.heap = onlyBlocks;
  },

  fit: function(blocks) {
    //
    // Loop through all the blocks, looking for a heapBlock
    // that it can fit into.
    //
    this.heap = [{x0:0,y0:0,x1:this._root.w, y1: this._root.h}];
    var n, node, block;
    for (n = 0; n < blocks.length; n++) {
      block = blocks[n];
      block.rotate = false;
      if (this.findInHeap(block)) {  
        this.adjustHeap(block);
      } else {
        // If the block didn't fit in its current orientation,
        // rotate its dimensions and look again.
        block.w = block.h + (block.h = block.w, 0);
        block.rotate = true;
        if (this.findInHeap(block)) {
          this.adjustHeap(block);
        }
      }
    }  
  },

  findInHeap: function(block) {
    //
    // Find a heapBlock that can contain the block.
    //
    for (let i = 0; i < this.heap.length; i++) {
      let heapBlock = this.heap[i];
      if (heapBlock && block.w <= heapBlock.x1 - heapBlock.x0 && block.h <= heapBlock.y1 - heapBlock.y0) {
        block.x0 = heapBlock.x0;
        block.y0 = heapBlock.y0;
        block.x1 = heapBlock.x0 + block.w;
        block.y1 = heapBlock.y0 + block.h;
        return true;
      }
    }
    return false;
  },

  adjustHeap:  function(block) {
    //
    // Find all heap entries that intersect with block,
    // and adjust the heap by breaking up the heapBlock
    // into the possible 4 blocks that remain after
    // removing the intersecting portion.
    //
    let n = this.heap.length;
    for (let i = 0; i < n; i++) {
      let heapBlock = this.heap[i];
      let overlap = this.intersect(heapBlock, block);
      if (overlap) {

        // Top
        if (overlap.y1 !== heapBlock.y1) {
          this.heap.push({
            x0: heapBlock.x0,
            y0: overlap.y1,
            x1: heapBlock.x1,
            y1: heapBlock.y1
          });
        }

        // Right
        if (overlap.x1 !== heapBlock.x1) {
          this.heap.push({
            x0: overlap.x1,
            y0: heapBlock.y0,
            x1: heapBlock.x1,
            y1: heapBlock.y1
          });
        }

        // Bottom
        if (heapBlock.y0 !== overlap.y0) {
          this.heap.push({
            x0: heapBlock.x0,
            y0: heapBlock.y0,
            x1: heapBlock.x1,
            y1: overlap.y0
          });
        }

        // Left
        if (heapBlock.x0 != overlap.x0) {
          this.heap.push({
            x0: heapBlock.x0,
            y0: heapBlock.y0,
            x1: overlap.x0,
            y1: heapBlock.y1
          });
        }       

        this.heap[i] = null;
      }
    }

    this.unionAll();
  }

}
…将离开
p.heap
,如下所示

The final HEAP
[{"x0":2100,"y0":940,"x1":2400,"y1":1200},
{"x0":2300,"y0":500,"x1":2400,"y1":1200},
{"x0":2250,"y0":0,"x1":2300,"y1":200}]

The final BLOCKS
[{"w":2100,"h":600,"rotate":false,"x0":0,"y0":0,"x1":2100,"y1":600},
{"w":2100,"h":600,"rotate":false,"x0":0,"y0":600,"x1":2100,"y1":1200},
{"w":150,"h":200,"rotate":false,"x0":2100,"y0":0,"x1":2250,"y1":200},
{"w":200,"h":740,"rotate":true,"x0":2100,"y0":200,"x1":2300,"y1":940},
{"w":100,"h":500,"rotate":true,"x0":2300,"y0":0,"x1":2400,"y1":500}]
请注意,此算法未经优化。即,它不会对传入块进行排序(即,按宽度、高度或面积等),也不会在执行
unionAll
后对堆进行排序,这会将堆减少到最大大小的堆块列表。(即,在每次调用
unionAll
后,都有机会按宽度、高度或面积等对堆进行排序,以便在堆中搜索下一个要放置的可用块时,如果堆从最大到最小排序,则算法会将块放置在最大的可用堆块中,或者如果从最小到最大排序,则块将被在任何情况下,都将把这些类型的优化作为练习


另外,请以怀疑的态度看待此算法,并进行适当的测试。

非常感谢,但我已经测试过,当帧大小为800x600时它似乎可以工作,如果我将帧尺寸反转为600x800,它将不工作,并且会说它不适合。对于2400x1200,我已经使用2100 x 6002100 x 600150 x 200200 x 740的块进行了测试这不起作用。如果我更改面板尺寸ro 1200x2400,它会工作得很好。任何建议我运行了800x600和600x800与700x150和150x700的所有四种组合,它们都导致了配合。可能导致问题的原因是,在重新运行另一个配合之前,如果您使用的是相同的封隔器变量,则需要重新初始化在执行
myPackerVar.fit(…)之前,将其编译为.Ie,
myPackerVar.init(800600)
。问题是,如果执行两个
myPackerVar.fit
连续运行,则
this.root
变量具有上次运行的剩余信息。我想我看到了问题。在2400x1200的第二个示例中,算法用2100 x 600的两个块填充空间,留下两个300 x 600的区域。虽然相邻的,算法中没有任何东西可以将它们组合成600x1200块,因此200x740块没有
p = new Packer(2400,1200);
blocks = [{w:2100,h:600},{w:2100,h:600},{w:150,h:200},{w:740,h:200},{w:500,h:100}];
p.fit(blocks);
The final HEAP
[{"x0":2100,"y0":940,"x1":2400,"y1":1200},
{"x0":2300,"y0":500,"x1":2400,"y1":1200},
{"x0":2250,"y0":0,"x1":2300,"y1":200}]

The final BLOCKS
[{"w":2100,"h":600,"rotate":false,"x0":0,"y0":0,"x1":2100,"y1":600},
{"w":2100,"h":600,"rotate":false,"x0":0,"y0":600,"x1":2100,"y1":1200},
{"w":150,"h":200,"rotate":false,"x0":2100,"y0":0,"x1":2250,"y1":200},
{"w":200,"h":740,"rotate":true,"x0":2100,"y0":200,"x1":2300,"y1":940},
{"w":100,"h":500,"rotate":true,"x0":2300,"y0":0,"x1":2400,"y1":500}]