layout-umd

An improved UMD build of layout

Этот скрипт недоступен для установки пользователем. Он является библиотекой, которая подключается к другим скриптам мета-ключом // @require https://update.greasyfork.org/scripts/527730/1541454/layout-umd.js

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.layout = factory());
})(this, (() => {
    const binpack = (() => {
        class GrowingPacker {
            constructor() { }
            fit(blocks) {
                var n, node, block, len = blocks.length, fit;
                var width = len > 0 ? blocks[0].width : 0;
                var height = len > 0 ? blocks[0].height : 0;
                this.root = { x: 0, y: 0, width: width, height: height };
                for (n = 0; n < len; n++) {
                    block = blocks[n];
                    if (node = this.findNode(this.root, block.width, block.height)) {
                        fit = this.splitNode(node, block.width, block.height);
                        block.x = fit.x;
                        block.y = fit.y;
                    }
                    else {
                        fit = this.growNode(block.width, block.height);
                        block.x = fit.x;
                        block.y = fit.y;
                    }
                }
            }
            findNode(root, width, height) {
                if (root.used)
                    return this.findNode(root.right, width, height) || this.findNode(root.down, width, height);
                else if ((width <= root.width) && (height <= root.height))
                    return root;
                else
                    return null;
            }
            splitNode(node, width, height) {
                node.used = true;
                node.down = { x: node.x, y: node.y + height, width: node.width, height: node.height - height };
                node.right = { x: node.x + width, y: node.y, width: node.width - width, height: height };
                return node;
            }
            growNode(width, height) {
                var canGrowDown = (width <= this.root.width);
                var canGrowRight = (height <= this.root.height);
                var shouldGrowRight = canGrowRight && (this.root.height >= (this.root.width + width)); // attempt to keep square-ish by growing right when height is much greater than width
                var shouldGrowDown = canGrowDown && (this.root.width >= (this.root.height + height)); // attempt to keep square-ish by growing down  when width  is much greater than height
                if (shouldGrowRight)
                    return this.growRight(width, height);
                else if (shouldGrowDown)
                    return this.growDown(width, height);
                else if (canGrowRight)
                    return this.growRight(width, height);
                else if (canGrowDown)
                    return this.growDown(width, height);
                else
                    return null; // need to ensure sensible root starting size to avoid this happening
            }
            growRight(width, height) {
                this.root = {
                    used: true,
                    x: 0,
                    y: 0,
                    width: this.root.width + width,
                    height: this.root.height,
                    down: this.root,
                    right: { x: this.root.width, y: 0, width: width, height: this.root.height }
                };
                var node;
                if (node = this.findNode(this.root, width, height))
                    return this.splitNode(node, width, height);
                else
                    return null;
            }
            growDown(width, height) {
                this.root = {
                    used: true,
                    x: 0,
                    y: 0,
                    width: this.root.width,
                    height: this.root.height + height,
                    down: { x: 0, y: this.root.height, width: this.root.width, height: height },
                    right: this.root
                };
                var node;
                if (node = this.findNode(this.root, width, height))
                    return this.splitNode(node, width, height);
                else
                    return null;
            }
        }
        return (items, options) => {
            options = options || {};
            var packer = new GrowingPacker();
            var inPlace = options.inPlace || false;
            var newItems = items.map(item => inPlace ? item : { width: item.width, height: item.height, item: item });
            newItems = newItems.toSorted((a, b) => (b.width * b.height) - (a.width * a.height));
            packer.fit(newItems);
            const ret = {
                width: newItems.reduce((curr, item) => Math.max(curr, item.x + item.width), 0),
                height: newItems.reduce((curr, item) => Math.max(curr, item.y + item.height), 0)
            };
            if (!inPlace) {
                ret.items = newItems;
            }
            return ret;
        };
    })();
    const algorithms = {
        'top-down': {
            sort: items => items.toSorted((a, b) => a.height - b.height),
            placeItems: items => {
                let y = 0;
                items.forEach(item => {
                    item.x = 0;
                    item.y = y;
                    y += item.height;
                });
                return items;
            }
        },
        'left-right': {
            sort: items => items.toSorted((a, b) => a.width - b.width),
            placeItems: items => {
                var x = 0;
                items.forEach(item => {
                    item.x = x;
                    item.y = 0;
                    x += item.width;
                });
                return items;
            }
        },
        diagonal: {
            sort: items => items.toSorted((a, b) => {
                const aDiag = Math.sqrt(Math.pow(a.height, 2) + Math.pow(a.width, 2));
                const bDiag = Math.sqrt(Math.pow(b.height, 2) + Math.pow(b.width, 2));
                return aDiag - bDiag;
            }),
            placeItems: items => {
                let x = 0;
                let y = 0;
                items.forEach(item => {
                    item.x = x;
                    item.y = y;
                    x += item.width;
                    y += item.height;
                });
            }
        },
        'alt-diagonal': {
            sort: items => items.toSorted((a, b) => {
                const aDiag = Math.sqrt(Math.pow(a.height, 2) + Math.pow(a.width, 2));
                const bDiag = Math.sqrt(Math.pow(b.height, 2) + Math.pow(b.width, 2));
                return aDiag - bDiag;
            }),
            placeItems: items => {
                let x = 0;
                let y = 0;
                items.forEach(item => {
                    item.x = x - item.width;
                    item.y = y;
                    x += item.width;
                    y += item.height;
                });
            }
        },
        'binary-tree': {
            sort: items => items,
            placeItems: items => {
                binpack(items, {inPlace: true});
                return items
            }
        }
    };
    class PackingSmith {
        constructor(algorithm, options) {
            this.items = [];
            this.algorithm = algorithm;
            options = options || {};
            var sort = options.sort !== undefined ? options.sort : true;
            this.sort = sort;
        }
        addItem(item) {
            this.items.push(item);
        }
        normalizeCoordinates() {
            var items = this.items;
            var minX = Infinity;
            var minY = Infinity;
            items.forEach(item => {
                var coords = item;
                minX = Math.min(minX, coords.x);
                minY = Math.min(minY, coords.y);
            });
            items.forEach(item => {
                var coords = item;
                coords.x -= minX;
                coords.y -= minY;
            });
        }
        getStats() {
            const { x, y } = this.items.reduce((acc, item) => {
                acc.x.min.push(item.x);
                acc.y.min.push(item.y);
                acc.x.max.push(item.x + item.width);
                acc.y.max.push(item.y + item.height);
                return acc;
            }, {
                x: { min: [], max: [] },
                y: { min: [], max: [] }
            });
            return {
                minX: Math.max(...x.min),
                maxX: Math.max(...x.max),
                maxY: Math.max(...y.max),
                minY: Math.max(...y.min)
            }
        }
        getItems() {
            return this.items;
        }
        processItems() {
            var items = this.items;
            if (this.sort) {
                items = this.algorithm.sort(items);
            }
            items = this.algorithm.placeItems(items);
            this.items = items;
            return items;
        }
        exportItems() {
            this.processItems();
            this.normalizeCoordinates();
            return this.items;
        }
        export() {
            var items = this.exportItems();
            var stats = this.getStats();
            var retObj = {
                'height': stats.maxY,
                'width': stats.maxX,
                'items': items
            };
            return retObj;
        }
    }
    function Layout(algorithmName, options) {
        var algorithm = algorithmName || 'top-down';
        if (typeof algorithm === 'string') {
            algorithm = algorithms[algorithmName];
        }
        var retSmith = new PackingSmith(algorithm, options);
        return retSmith;
    }
    Layout.PackingSmith = PackingSmith;
    function addAlgorithm(name, algorithm) {
        algorithms[name] = algorithm;
    }
    Layout.addAlgorithm = addAlgorithm;
    Layout.algorithms = algorithms;
    return Layout;
}));