<commit_before>import { BBox, Vec2, WorkerMessage } from '../types';
import { stride, offsets } from '../markerArray';

const collideBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
const marginBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
const degradationBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };

export function generalize(data: WorkerMessage) {
    const { bounds, pixelRatio, priorityGroups, sprites, markers, markerCount } = data;

    const width = (bounds.maxX - bounds.minX >> 3) + 1 << 3; // Ширина должна быть кратна 8
    const height = bounds.maxY - bounds.minY;

    const planeLength = width * height + 8 >> 3;

    /**
     * Алгоритм действует по принципу закрашивания плоскости маркерами.
     * Плоскость – массив, каждый элемент которого представляет собой пиксель на экране.
     * Для ускорения процесса используется битовой массив, 1 бит – 1 пиксель.
     * Всего у нас есть три плоскости:
     */

    // Это первая, с помощью нее мы просто проверяем попадание маркеров с safeZone и вставляем их с margin
    const plane = new Uint8Array(planeLength);

    /**
     * Следующие две используются для проверки на деградацию маркеров.
     * Область деградации маркера текущей группы действует только на маркера из следующей группы
     * и не влияет на другие маркера текущей группы.
     * Поэтому их две:
     */

    // Одна – для проверки текущей группы с предыдущими
    const currentDegradationPlane = new Uint8Array(planeLength);
    // Вторая – для передачи в следующие группы
    const degradationPlane = new Uint8Array(planeLength);

    /**
     * Одни и те же маркера могут участвовать в генерализации несколько раз,
     * такие маркера имеют поле prevGroupIndex.
     *
     * Чтобы не делать лишнюю работу, и чтобы результат генерализации был устойчив,
     * мы используем результаты предыдущей генерализации.
     */

    // Поэтому вначале закрашиваем плоскость повторно генерализуемыми маркерами
    for (let i = 0; i < markerCount; i++) {
        const prevGroupIndex = markers[i * stride + offsets.prevGroupIndex];
        const pixelPositionX = markers[i * stride + offsets.pixelPositionX];
        const pixelPositionY = markers[i * stride + offsets.pixelPositionY];

        // prevGroupIndex не равен NaN, если маркер уже проходил генерализацию
        if (!isNaN(prevGroupIndex)) {
            const { iconIndex, margin, degradation } = priorityGroups[prevGroupIndex];
            const sprite = sprites[iconIndex];

            if (!sprite) {
                // smth shit
                continue;
            }

            const { size, anchor, pixelDensity } = sprite;

            // Вставляем их на основную плоскость и плоскость деградции без всяких проверок
            createBBox(marginBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, margin);

            if (!bboxIsEmpty(marginBBox)) {
                putToArray(plane, width, marginBBox);
            }

            createBBox(degradationBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, degradation);

            if (!bboxIsEmpty(degradationBBox)) {
                putToArray(degradationPlane, width, degradationBBox);
            }
        }
    }

    // Здесь начинает работу основной алгоритм генерализации.
    // У нас два вложенных цикла: по группам -> по маркерам.
    for (let i = 0; i < priorityGroups.length; i++) {
        const group = priorityGroups[i];
        const { safeZone, iconIndex, margin, degradation } = group;
        const sprite = sprites[iconIndex];

        if (!sprite) {
            // smth shit
            continue;
        }

        const { size, anchor, pixelDensity } = sprite;

        // Копируем область деградации от предудщих групп,
        // чтобы маркера текущей группы не влияли друг на друга.
        currentDegradationPlane.set(degradationPlane);

        for (let j = 0; j < markerCount; j++) {
            const markerOffset = j * stride;

            const groupIndex = markers[markerOffset + offsets.groupIndex];
            const prevGroupIndex = markers[markerOffset + offsets.prevGroupIndex];
            const pixelPositionX = markers[markerOffset + offsets.pixelPositionX];
            const pixelPositionY = markers[markerOffset + offsets.pixelPositionY];

            // Пропускаем маркера, чей изначальный groupIndex больше индекса текущей перебираемой группы.
            // Такие маркера будут проверены в следующах группах.
            // Также пропускаем повторно генерализуемые маркера.
            if (groupIndex > i || !isNaN(prevGroupIndex)) {
                continue;
            }

            // Маркер первый раз попал в область деградации – пропускаем
            createBBox(marginBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, margin);
            if (bboxIsEmpty(marginBBox) ||
                (groupIndex === i && collide(currentDegradationPlane, width, marginBBox))
            ) {
                continue;
            }

            // Область маркера пересекает область уже вставшего маркера – пропускаем
            createBBox(collideBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, safeZone);
            if (bboxIsEmpty(collideBBox)) {
                continue;
            }

            if (!collide(plane, width, collideBBox)) {
                createBBox(degradationBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                    pixelPositionX, pixelPositionY, degradation);

                // Если все хорошо и маркер выжил, закрашиваем его в двух плоскостях
                putToArray(plane, width, marginBBox);
                putToArray(degradationPlane, width, degradationBBox);

                markers[markerOffset + offsets.iconIndex] = iconIndex;
                markers[markerOffset + offsets.prevGroupIndex] = i;
            }
        }
    }
};

/**
 * Проверяет, пересекает ли область что-либо в плоскости
 *
 * @param {Uint8Array} arr Плоскость
 * @param {number} width Ширина плоскости
 * @param {BBox} bbox Проверяемая область
 * @returns {boolean}
 */
function collide(arr: Uint8Array, width: number, bbox: BBox): boolean {
    const x1 = bbox.minX;
    const y1 = bbox.minY;<commit_msg>Fix lint error<commit_after>import { BBox, Vec2, WorkerMessage } from '../types';
import { stride, offsets } from '../markerArray';

const collideBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
const marginBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
const degradationBBox: BBox = { minX: 0, minY: 0, maxX: 0, maxY: 0 };

export function generalize(data: WorkerMessage) {
    const { bounds, pixelRatio, priorityGroups, sprites, markers, markerCount } = data;

    const width = (bounds.maxX - bounds.minX >> 3) + 1 << 3; // Ширина должна быть кратна 8
    const height = bounds.maxY - bounds.minY;

    const planeLength = width * height + 8 >> 3;

    /**
     * Алгоритм действует по принципу закрашивания плоскости маркерами.
     * Плоскость – массив, каждый элемент которого представляет собой пиксель на экране.
     * Для ускорения процесса используется битовой массив, 1 бит – 1 пиксель.
     * Всего у нас есть три плоскости:
     */

    // Это первая, с помощью нее мы просто проверяем попадание маркеров с safeZone и вставляем их с margin
    const plane = new Uint8Array(planeLength);

    /**
     * Следующие две используются для проверки на деградацию маркеров.
     * Область деградации маркера текущей группы действует только на маркера из следующей группы
     * и не влияет на другие маркера текущей группы.
     * Поэтому их две:
     */

    // Одна – для проверки текущей группы с предыдущими
    const currentDegradationPlane = new Uint8Array(planeLength);
    // Вторая – для передачи в следующие группы
    const degradationPlane = new Uint8Array(planeLength);

    /**
     * Одни и те же маркера могут участвовать в генерализации несколько раз,
     * такие маркера имеют поле prevGroupIndex.
     *
     * Чтобы не делать лишнюю работу, и чтобы результат генерализации был устойчив,
     * мы используем результаты предыдущей генерализации.
     */

    // Поэтому вначале закрашиваем плоскость повторно генерализуемыми маркерами
    for (let i = 0; i < markerCount; i++) {
        const prevGroupIndex = markers[i * stride + offsets.prevGroupIndex];
        const pixelPositionX = markers[i * stride + offsets.pixelPositionX];
        const pixelPositionY = markers[i * stride + offsets.pixelPositionY];

        // prevGroupIndex не равен NaN, если маркер уже проходил генерализацию
        if (!isNaN(prevGroupIndex)) {
            const { iconIndex, margin, degradation } = priorityGroups[prevGroupIndex];
            const sprite = sprites[iconIndex];

            if (!sprite) {
                // smth shit
                continue;
            }

            const { size, anchor, pixelDensity } = sprite;

            // Вставляем их на основную плоскость и плоскость деградции без всяких проверок
            createBBox(marginBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, margin);

            if (!bboxIsEmpty(marginBBox)) {
                putToArray(plane, width, marginBBox);
            }

            createBBox(degradationBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, degradation);

            if (!bboxIsEmpty(degradationBBox)) {
                putToArray(degradationPlane, width, degradationBBox);
            }
        }
    }

    // Здесь начинает работу основной алгоритм генерализации.
    // У нас два вложенных цикла: по группам -> по маркерам.
    for (let i = 0; i < priorityGroups.length; i++) {
        const group = priorityGroups[i];
        const { safeZone, iconIndex, margin, degradation } = group;
        const sprite = sprites[iconIndex];

        if (!sprite) {
            // smth shit
            continue;
        }

        const { size, anchor, pixelDensity } = sprite;

        // Копируем область деградации от предудщих групп,
        // чтобы маркера текущей группы не влияли друг на друга.
        currentDegradationPlane.set(degradationPlane);

        for (let j = 0; j < markerCount; j++) {
            const markerOffset = j * stride;

            const groupIndex = markers[markerOffset + offsets.groupIndex];
            const prevGroupIndex = markers[markerOffset + offsets.prevGroupIndex];
            const pixelPositionX = markers[markerOffset + offsets.pixelPositionX];
            const pixelPositionY = markers[markerOffset + offsets.pixelPositionY];

            // Пропускаем маркера, чей изначальный groupIndex больше индекса текущей перебираемой группы.
            // Такие маркера будут проверены в следующах группах.
            // Также пропускаем повторно генерализуемые маркера.
            if (groupIndex > i || !isNaN(prevGroupIndex)) {
                continue;
            }

            // Маркер первый раз попал в область деградации – пропускаем
            createBBox(marginBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, margin);
            if (bboxIsEmpty(marginBBox) ||
                (groupIndex === i && collide(currentDegradationPlane, width, marginBBox))
            ) {
                continue;
            }

            // Область маркера пересекает область уже вставшего маркера – пропускаем
            createBBox(collideBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                pixelPositionX, pixelPositionY, safeZone);
            if (bboxIsEmpty(collideBBox)) {
                continue;
            }

            if (!collide(plane, width, collideBBox)) {
                createBBox(degradationBBox, width, height, pixelRatio, size, anchor, pixelDensity,
                    pixelPositionX, pixelPositionY, degradation);

                // Если все хорошо и маркер выжил, закрашиваем его в двух плоскостях
                putToArray(plane, width, marginBBox);
                putToArray(degradationPlane, width, degradationBBox);

                markers[markerOffset + offsets.iconIndex] = iconIndex;
                markers[markerOffset + offsets.prevGroupIndex] = i;
            }
        }
    }
}

/**
 * Проверяет, пересекает ли область что-либо в плоскости
 *
 * @param {Uint8Array} arr Плоскость
 * @param {number} width Ширина плоскости
 * @param {BBox} bbox Проверяемая область
 * @returns {boolean}
 */
function collide(arr: Uint8Array, width: number, bbox: BBox): boolean {
    const x1 = bbox.minX;
    const y1 = bbox.minY;
{'fraction_non_alphanumeric': 0.08474178403755868, 'fraction_numerical': 0.00485133020344288, 'mean_word_length': 2.421954484605087, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4005408', 'n_tokens_mistral': 4138, 'n_tokens_neox': 4428, 'n_words': 1113}