UNPKG

@image/packer

Version:
131 lines (113 loc) 3.54 kB
export interface ISize { width: number, height: number } export interface IRect { x: number; y: number; width: number; height: number, data: ISize; } export interface INode { x: number; y: number; width: number; height: number, children?: INode[] } export class MultiBinPacker { public readonly maxWidth: number; public readonly maxHeight: number; public readonly padding?: number; public readonly bins: BinaryTreeBin[] = []; public readonly oversizedElements: ISize[] = []; constructor(maxWidth: number, maxHeight: number, padding?: number) { this.maxWidth = maxWidth; this.maxHeight = maxHeight; this.padding = padding; } public add(data: ISize) { if (data.width > this.maxWidth || data.height > this.maxHeight) { this.oversizedElements.push(data); } else { const added = this.bins.find(bin => !!bin.add(data)); if (!added) { const bin = new BinaryTreeBin(this.maxWidth, this.maxHeight, this.padding); bin.add(data); this.bins.push(bin); } } } private sort(rects: ISize[]) { return rects.slice().sort((a, b) => Math.max(b.width, b.height) - Math.max(a.width, a.height)); } public addArray(rects: ISize[]) { this.sort(rects).forEach(rect => this.add(rect)); } } export class BinaryTreeBin { private maxWidth: number; private maxHeight: number; private padding: number; private rootNode: INode; public width: number; public height: number; public rects: IRect[]; constructor(maxWidth: number, maxHeight: number, padding?: number) { this.width = 0; this.height = 0; this.maxWidth = maxWidth; this.maxHeight = maxHeight; this.padding = padding || 0; this.rootNode = {x: 0, y: 0, width: maxWidth + this.padding, height: maxHeight + this.padding}; this.rects = []; } public add(data: ISize): IRect | undefined { const node = this.findNode(this.rootNode, data.width + this.padding, data.height + this.padding); if (node) { node.children = this.createChildren(node, data.width + this.padding, data.height + this.padding); this.width = Math.max(this.width, node.x + data.width); this.height = Math.max(this.height, node.y + data.height); const rect: IRect = {width: data.width, height: data.height, x: node.x, y: node.y, data}; this.rects.push(rect); return rect; } return undefined; } private findNode(node: INode, width: number, height: number): INode | undefined { if (node.children) { for (let i = 0; i < node.children.length; i++) { const found = this.findNode(node.children[i], width, height); if (found) { return found; } } return undefined; } if ((width <= node.width) && (height <= node.height)) { return node; } return undefined; } private createChildren(node: INode, width: number, height: number): INode[] { const children = []; if (node.height - height > 0 && node.x < this.maxWidth) { children.push({ x: node.x, y: node.y + height, width: node.width, height: node.height - height }); } if (node.width - width > 0 && node.y < this.maxHeight) { children.push({ x: node.x + width, y: node.y, width: node.width - width, height: height }); } return children; } }