# Source: helpers/render.js

    /**
     * Helper functions targeted at rendering operations
     * @module
     * @private
    */

    /**
     * A very simple function aimed at scatter plots: attempts to coalesce "low-significance" SNPs that are too close to
     *  visually distinguish, thus creating a dataset with fewer points that can be rendered more quickly.
     *
     *  This depends on the strong and explicit assumption that points are ordered (typically in x position), so that
     *    nearby points can be grouped by iterating over the data in sequence.
     *
     * @param {Object[]} data Plot data, annotated with calculated `xc` and `yc` symbols for x and y coordinates (in px).
     * @param {Number} x_min The smallest x value of an "insignificant region" rectangle
     * @param {Number} x_max The largest x value of an "insignificant region" rectangle
     * @param {Number} x_gap Max px distance, in x direction, from the first point in a set, to qualify for grouping
     * @param {Number} y_min The smallest y value of an "insignificant region" rectangle
     * @param {Number} y_max The largest y value of an "insignificant region" rectangle
     * @param {Number} y_gap Max px distance, in y direction, from the first point in a set, to qualify for grouping
     * @return {Object[]} The simplified dataset with fewer points
     */
    function coalesce_scatter_points (data, x_min, x_max, x_gap, y_min, y_max, y_gap) {
        let final_data = [];

        const xcs = Symbol.for('lzX');
        const ycs = Symbol.for('lzY');

        let x_start = null;
        let y_start = null;
        let current_group = [];

        function _combine () {
            if (current_group.length) {
                // If there are points near each other, return the middle item to represent the group
                // We use a real point (rather than a synthetic average point) to best handle extra fields
                const item = current_group[Math.floor((current_group.length - 1) / 2)];
                final_data.push(item);
            }
            x_start = y_start = null;
            current_group = [];
        }

        function _start_run(x, y, item) {
            x_start = x;
            y_start = y;
            current_group.push(item);
        }

        data.forEach((item) => {
            const x = item[xcs];
            const y = item[ycs];

            const in_combine_region = (x >= x_min && x <= x_max && y >= y_min && y <= y_max);
            if (item.lz_is_match || !in_combine_region) {
                // If an item is marked as interesting in some way, always render it explicitly
                // (and coalesce the preceding points if a run was in progress, to preserve ordering)
                _combine();
                final_data.push(item);
            } else if (x_start === null) {
                // If not tracking a group, start tracking
                _start_run(x, y, item);
            } else {
                // Otherwise, the decision to render the point depends on whether it is close to a run of other
                //  insignificant points
                const near_prior = Math.abs(x - x_start) <= x_gap && Math.abs(y - y_start) <= y_gap;

                if (near_prior) {
                    current_group.push(item);
                } else {
                    // "if in combine region, and not near a prior point, coalesce all prior items, then track this point
                    //  as part of the next run that could be grouped"
                    _combine();
                    _start_run(x, y, item);
                }
            }
        });
        // At the end of the dataset, check whether any runs of adjacent points were in progress, and coalesce if so
        _combine();

        return final_data;
    }

    export { coalesce_scatter_points };