Initial commit
This commit is contained in:
117
node_modules/potpack/index.js
generated
vendored
Normal file
117
node_modules/potpack/index.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @typedef {Object} PotpackBox
|
||||
* @property {number} w Box width.
|
||||
* @property {number} h Box height.
|
||||
* @property {number} [x] X coordinate in the resulting container.
|
||||
* @property {number} [y] Y coordinate in the resulting container.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PotpackStats
|
||||
* @property {number} w Width of the resulting container.
|
||||
* @property {number} h Height of the resulting container.
|
||||
* @property {number} fill The space utilization value (0 to 1). Higher is better.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Packs 2D rectangles into a near-square container.
|
||||
*
|
||||
* Mutates the {@link boxes} array: it's sorted (by height/width),
|
||||
* and box objects are augmented with `x`, `y` coordinates.
|
||||
*
|
||||
* @param {PotpackBox[]} boxes
|
||||
* @return {PotpackStats}
|
||||
*/
|
||||
export default function potpack(boxes) {
|
||||
|
||||
// calculate total box area and maximum box width
|
||||
let area = 0;
|
||||
let maxWidth = 0;
|
||||
|
||||
for (const box of boxes) {
|
||||
area += box.w * box.h;
|
||||
maxWidth = Math.max(maxWidth, box.w);
|
||||
}
|
||||
|
||||
// sort the boxes for insertion by height, descending
|
||||
boxes.sort((a, b) => b.h - a.h);
|
||||
|
||||
// aim for a squarish resulting container,
|
||||
// slightly adjusted for sub-100% space utilization
|
||||
const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
|
||||
|
||||
// start with a single empty space, unbounded at the bottom
|
||||
const spaces = [{x: 0, y: 0, w: startWidth, h: Infinity}];
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
|
||||
for (const box of boxes) {
|
||||
// look through spaces backwards so that we check smaller spaces first
|
||||
for (let i = spaces.length - 1; i >= 0; i--) {
|
||||
const space = spaces[i];
|
||||
|
||||
// look for empty spaces that can accommodate the current box
|
||||
if (box.w > space.w || box.h > space.h) continue;
|
||||
|
||||
// found the space; add the box to its top-left corner
|
||||
// |-------|-------|
|
||||
// | box | |
|
||||
// |_______| |
|
||||
// | space |
|
||||
// |_______________|
|
||||
box.x = space.x;
|
||||
box.y = space.y;
|
||||
|
||||
height = Math.max(height, box.y + box.h);
|
||||
width = Math.max(width, box.x + box.w);
|
||||
|
||||
if (box.w === space.w && box.h === space.h) {
|
||||
// space matches the box exactly; remove it
|
||||
const last = spaces.pop();
|
||||
if (last && i < spaces.length) spaces[i] = last;
|
||||
|
||||
} else if (box.h === space.h) {
|
||||
// space matches the box height; update it accordingly
|
||||
// |-------|---------------|
|
||||
// | box | updated space |
|
||||
// |_______|_______________|
|
||||
space.x += box.w;
|
||||
space.w -= box.w;
|
||||
|
||||
} else if (box.w === space.w) {
|
||||
// space matches the box width; update it accordingly
|
||||
// |---------------|
|
||||
// | box |
|
||||
// |_______________|
|
||||
// | updated space |
|
||||
// |_______________|
|
||||
space.y += box.h;
|
||||
space.h -= box.h;
|
||||
|
||||
} else {
|
||||
// otherwise the box splits the space into two spaces
|
||||
// |-------|-----------|
|
||||
// | box | new space |
|
||||
// |_______|___________|
|
||||
// | updated space |
|
||||
// |___________________|
|
||||
spaces.push({
|
||||
x: space.x + box.w,
|
||||
y: space.y,
|
||||
w: space.w - box.w,
|
||||
h: box.h
|
||||
});
|
||||
space.y += box.h;
|
||||
space.h -= box.h;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
w: width, // container width
|
||||
h: height, // container height
|
||||
fill: (area / (width * height)) || 0 // space utilization
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user