util_color_color_gradient.js

import Color from "./color.js";
import Util from "../misc/util.js";

/**
 * @module ColorGradient
 * @fileoverview Contains ColorGradient class.
 */

/**
 * @class
 * Used to hold Color values in a gradient form. Colors can be added to the gradient at
 * specific points from 0 to 1, and they (along with the colors in between) can be retrieved
 * at specified points.
 */
class ColorGradient {
    /**
     * Create a new ColorGradient.
     * @param {Object.<number, Color>} values Initial values of gradient
     * @param {boolean} snap (Optional) Whether the get() function should return interpolated values between the
     * colors (false), or snap to the color at the closest lower point (true). Set to false by default.
     * @constructor
     */
    constructor(values = {}, snap = false) {
        const keys = Object.keys(values).map(Number).sort((a, b) => a - b);

        this._points = [];
        this._colors = [];

        for (const key of keys) {
            this._points.push(key);
            this._colors.push(values[key.toString()].copy());
        }

        this.snap = snap;
    }

    /**
     * Set a Color at a point
     * @param {number} point Point to set the color at (0-1)
     * @param {Color} color Color to set at the point
     */
    set(point, color) {
        this._points.push(point);
        this._colors.push(color);
    }

    /**
     * Get the color on the gradient at a specified point.
     * @param {number} point Point along the gradient (0-1)
     * @returns {Color} Resulting color at the specified point along the gradient
     */
    get(point) {
        if (this._points.length === 0)
            return new Color(0, 0, 0);

        if (this._points.length === 1)
            return this._colors[0];

        if (point < this._points[0])
            return this._colors[0];

        if (point > this._points[this._points.length - 1])
            return this._colors[this._colors.length - 1];

        let last = 0;
        for (let i = 1; i < this._points.length; i++) {
            if (point < this._points[i]) {
                if (this.snap)
                    return this._colors[last];

                let alpha = (point - this._points[last]) / (this._points[i] - this._points[last]);
                return this._colors[last].lerp(this._colors[i], alpha);
            }

            last = i;
        }
    }
}

export default ColorGradient;