{"version":3,"file":"index.cjs","sources":["../src/index.js"],"sourcesContent":["export const MAX_CANVAS_SIZE = 10000;\n\n/**\n * @callback AttachCanvasUpdate\n * @param {HTMLCanvasElement} canvas\n * @param {number} width\n * @param {number} height\n * @returns {void}\n **/\n\n/**\n * @typedef {object} AttachCanvasOptions\n * @prop {HTMLElement=} container - Which element contains the canvas; will be used to compute\n *   the dimensions of the canvas.\n * @prop {AttachCanvasUpdate=} onResize - A callback function which will be called whenever the\n *   dimensions of the canvas change.\n * @prop {number=} downscale - An integer to downscale the canvas by,\n *   can be used to render the canvas at a lower resolution.\n **/\n\n/**\n * A function to call to unbind the event listeners attached to a canvas through `attachCanvas`.\n * *DEPRECATION NOTICE:* This type will be removed in the next major release\n * @callback AttachCanvasUnbind\n * @returns {void}\n **/\n\n/**\n * Makes the given canvas pixel-perfect, by dynamically resizing it\n * in order to have the canvas pixel resolution be always equal to the device resolution.\n *\n * Returns a function to \"unbind\" the bound listeners.\n *\n * @param {HTMLCanvasElement} canvas\n * @param {AttachCanvasOptions} options\n * @returns {() => void}\n **/\nexport function attachCanvas(canvas, options = {}) {\n    const EPSILON = 0.001;\n    canvas.style.imageRendering = \"pixelated\";\n    const container = options.container ?? canvas.parentNode;\n    const onResize = options.onResize ?? (() => {});\n    const downscale = options.downscale ?? 1;\n\n    let old_width = null;\n    let old_height = null;\n    let old_dpr = null;\n\n    function resize(dpr) {\n        // Get dimensions of container\n        let {width, height} = container.getBoundingClientRect?.() ?? {\n            width: container.clientWidth,\n            height: container.clientHeight\n        };\n\n        // Compute native dimensions of canvas\n        width = width * dpr / downscale;\n        height = height * dpr / downscale;\n        if (isInteger(width, EPSILON) && isInteger(height, EPSILON)) {\n            width = Math.round(width);\n            height = Math.round(height);\n        } else {\n            width = Math.ceil(width);\n            height = Math.ceil(height);\n        }\n\n        if (width === old_width && height === old_height && dpr === old_dpr) {\n            // No onResize needed\n            return;\n        }\n\n        old_width = width;\n        old_height = height;\n        old_dpr = dpr;\n\n        // Safety measure for when the container size depends on the canvas' size\n        if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) {\n            throw new Error(\n                `Prevented resizing canvas to ${width}x${height}; `\n                + `is the container missing \"overflow: hidden\" or \"width\"/\"height\"?`\n            );\n        }\n\n        // Set image width and height\n        canvas.width = width;\n        canvas.height = height;\n\n        // Set style width to guarantee perfect scaling\n        canvas.style.width = `${width / dpr * downscale}px`;\n        canvas.style.height = `${height / dpr * downscale}px`;\n\n        onResize(canvas, width, height);\n    }\n\n    // Bind listeners\n\n    let unbindDpr = listenPixelRatio(canvas, (dpr) => {\n        resize(dpr);\n    });\n\n    let resizeObserver = new ResizeObserver((entries) => {\n        resize(window.devicePixelRatio ?? 1);\n    });\n\n    resizeObserver.observe(container, {\n        box: \"border-box\"\n    });\n\n    setTimeout(() => {\n        resize(window.devicePixelRatio ?? 1);\n    }, 0);\n\n    return function detachCanvas() {\n        if (resizeObserver) {\n            resizeObserver.disconnect();\n            // Drop resizeObserver\n            resizeObserver = null;\n        }\n\n        if (unbindDpr) {\n            unbindDpr();\n            // Drop unbindDpr\n            unbindDpr = null;\n        }\n    };\n}\n\nexport function listenPixelRatio(element, callback, fireFirst = false) {\n    let first = true;\n    let media;\n\n    function update() {\n        let dpr = window.devicePixelRatio ?? 1;\n        let dpr_str = (dpr * 100).toFixed(0);\n\n        if (!first && fireFirst) {\n            callback(dpr, element);\n        }\n        first = false;\n\n        media = matchMedia(`(resolution: ${dpr}dppx)`);\n\n        media.addEventListener(\"change\", update, {once: true});\n    }\n\n    update();\n\n    return function detachPixelRatio() {\n        media.removeEventListener(\"change\", update);\n    }\n}\n\n/**\n * @param {HTMLCanvasElement} canvas\n * @return {PixelPerfectContext2D}\n **/\nexport function getContext2D(canvas) {\n    return new PixelPerfectContext2D(canvas);\n}\n\n/**\n * @typedef {object} CoalescedTouch\n * @prop {number} x\n * @prop {number} y\n * @prop {PointerEvent['pressure']} pressure\n **/\n\n/**\n * @typedef {object} Touch\n * @prop {number} x\n * @prop {number} y\n * @prop {number} click_x\n * @prop {number} click_y\n * @prop {number} dx\n * @prop {number} dy\n * @prop {PointerEvent['pointerType']} type\n * @prop {number} id\n * @prop {PointerEvent['buttons']} buttons\n * @prop {PointerEvent['pressure']} pressure\n * @prop {CoalescedTouch[]} coalesced\n **/\n\n/**\n * @typedef {object} AttachTouchOptions\n * @prop {((touch: Touch, touchMap: Map<number, Touch>) => void)=} onDown\n * @prop {((affectedTouches: Touch[], touchMap: Map<number, Touch>) => void)=} onMove\n * @prop {((touch: Touch | undefined, touchMap: Map<number, Touch>) => void)=} onUp\n * @prop {number=} downscale\n * @prop {boolean=} preventDefault\n **/\n\n/**\n * @param {HTMLElement} element\n * @param {AttachTouchOptions} options\n **/\nexport function attachTouch(element, options = {}) {\n    /** @type {Map<PointerEvent['pointerId'], Touch>} **/\n    const touches = new Map();\n\n    const downscale = options.downscale ?? 1;\n\n    let queued = null;\n    let queuedTouches = [];\n\n    function get_dpr() {\n        return window.devicePixelRatio ?? 1;\n    }\n\n    /**\n     * @param {DOMRect} bounding\n     * @param {number} dpr\n     * @param {PointerEvent} event\n     **/\n    function getCoords(bounding, dpr, event) {\n        return [\n            (event.clientX - bounding.x) * dpr / downscale,\n            (event.clientY - bounding.y) * dpr / downscale\n        ];\n    }\n\n    /** @param {PointerEvent} event */\n    function onDown(event) {\n        if (options.preventDefault) event.preventDefault();\n\n        let dpr = get_dpr();\n        let bounding = element.getBoundingClientRect();\n\n        const [x, y] = getCoords(bounding, dpr, event);\n\n        let touch = {\n            x,\n            y,\n            click_x: x,\n            click_y: y,\n            dx: 0,\n            dy: 0,\n            type: event.pointerType,\n            id: event.pointerId,\n            buttons: event.buttons,\n            pressure: event.pressure,\n            coalesced: [],\n        };\n\n        touches.set(event.pointerId, touch);\n\n        options.onDown?.(touch, touches);\n    }\n\n    /** @param {PointerEvent} event */\n    function onMove(event) {\n        if (options.preventDefault) event.preventDefault();\n\n        let dpr = get_dpr();\n        let bounding = element.getBoundingClientRect();\n\n        const [x, y] = getCoords(bounding, dpr, event);\n\n        if (touches.has(event.pointerId)) {\n            let touch = cloneGet(touches, event.pointerId);\n\n            touch.dx = x - touch.x;\n            touch.dy = y - touch.y;\n\n            touch.x = x;\n            touch.y = y;\n            touch.coalesced = event.getCoalescedEvents().map(event => {\n                const [x, y] = getCoords(bounding, dpr, event);\n                return {\n                    x,\n                    y,\n                    pressure: event.pressure,\n                };\n            });\n\n            touch.pressure = event.pressure;\n\n            if (queued === null) {\n                // TODO: proper queue management system, group together coalesced events in the queue\n                queued = setTimeout(() => {\n                    try {\n                        options.onMove?.(queuedTouches, touches);\n                    } finally {\n                        queuedTouches = [];\n                        queued = null;\n                    }\n                }, 0);\n            }\n            queuedTouches.push(touch);\n        }\n    }\n\n    /** @param {PointerEvent} event */\n    function onUp(event) {\n        if (options.preventDefault) event.preventDefault();\n\n        let touch = touches.get(event.pointerId);\n\n        if (!touch) return;\n\n        let dpr = get_dpr();\n        let bounding = element.getBoundingClientRect();\n\n        let x = (event.clientX - bounding.x) * dpr / downscale;\n        let y = (event.clientY - bounding.y) * dpr / downscale;\n\n        touch.dx = x - touch.x;\n        touch.dy = y - touch.y;\n\n        touch.x = x;\n        touch.y = y;\n\n        touch.coalesced = event.getCoalescedEvents().map(event => {\n            const [x, y] = getCoords(bounding, dpr, event);\n            return {\n                x,\n                y,\n                pressure: event.pressure,\n            };\n        });\n\n        touch.pressure = event.pressure;\n\n        touches.delete(event.pointerId);\n\n        options.onUp?.(touch, touches);\n    }\n\n    /** @param {PointerEvent} event */\n    function onCancel(event) {\n        const touch = touches.get(event.pointerId);\n        touches.delete(event.pointerId);\n\n        options.onUp?.(touch, touches);\n    }\n\n    element.addEventListener(\"pointerdown\", onDown);\n    element.addEventListener(\"pointermove\", onMove);\n    element.addEventListener(\"pointerup\", onUp);\n    element.addEventListener(\"pointerout\", onUp);\n    element.addEventListener(\"pointercancel\", onCancel);\n\n    /** @param {PointerEvent} event */\n    function onTouchMove(event) {\n        event.preventDefault();\n    }\n\n    // preventDefault() on pointer events does not work as expected (eg. scrolling still happens), so this is needed instead\n    if (options.preventDefault) {\n        element.addEventListener(\"touchmove\", onTouchMove, {passive: false});\n    }\n\n    return function detachTouch() {\n        element.removeEventListener(\"pointerdown\", onDown);\n        element.removeEventListener(\"pointermove\", onMove);\n        element.removeEventListener(\"pointup\", onUp);\n        element.removeEventListener(\"pointercancel\", onCancel);\n\n        if (options.preventDefault) {\n            element.removeEventListener(\"touchmove\", onTouchMove, {passive: false});\n        }\n    }\n}\n\n/**\n * @typedef {object} PannableOptions\n * @prop {number=} downscale\n * @prop {number=} dx Defaults to 0\n * @prop {number=} dy Defaults to 0\n * @prop {number=} minScale\n * @prop {number=} maxScale\n * @prop {number=} scale Defaults to 1\n * @prop {boolean=} round Whether or not to round `dx` and `dy` in `getState`\n **/\n\n/**\n * Handles panning and zooming\n **/\nexport class Pannable {\n    /**\n     * @param {PannableOptions=} options\n     **/\n    constructor(options) {\n        /** @type {number} **/\n        this.dx = options?.dx || 0;\n        /** @type {number} **/\n        this.dy = options?.dy || 0;\n        /** @type {number} **/\n        this.logScale = options?.scale ? Math.log2(options.scale) : 0.0;\n        /** @type {number} **/\n        this.logMinScale = options?.minScale ? Math.log2(options.minScale) : -Infinity;\n        /** @type {number} **/\n        this.logMaxScale = options?.maxScale ? Math.log2(options.maxScale) : Infinity;\n\n        /** @private **/\n        this.lastValues = [0, 0, 1];\n\n        /** @private **/\n        this.round = options.round;\n    }\n\n    /** @private **/\n    _getValues(touches, cx = 0, cy = 0) {\n        // First-order moments of touches.x and touches.y\n        let mx = 0;\n        let my = 0;\n        // Second-order moments of touches.x and touches.y\n        let mx2 = 0;\n        let my2 = 0;\n\n        for (const touch of touches.values()) {\n            mx += touch.x;\n            my += touch.y;\n            mx2 += touch.x * touch.x;\n            my2 += touch.y * touch.y;\n        }\n\n        const n = touches.size;\n        if (n <= 1) {\n            return [mx - cx, my - cy, 1];\n        }\n\n        // Unbiased sample variance, from https://en.wikipedia.org/wiki/Bessel%27s_correction#Formula\n        const sx = mx2 / (n - 1) - (mx * mx) / (n * (n - 1));\n        const sy = my2 / (n - 1) - (my * my) / (n * (n - 1));\n\n        return [mx / n - cx, my / n - cy, Math.sqrt(sx + sy)];\n    }\n\n    /** @returns {number} **/\n    get scale() {\n        return Math.pow(2, this.logScale);\n    }\n\n    /** @returns {PannableState} **/\n    getState(cx = 0, cy = 0) {\n        if (this.round) {\n            return new PannableState(\n                Math.round(this.dx + cx),\n                Math.round(this.dy + cy),\n                this.logScale\n            );\n        } else {\n            return new PannableState(this.dx + cx, this.dy + cy, this.logScale);\n        }\n    }\n\n    /**\n     * @param {Map<number, Touch>} touches\n     **/\n    move(touches, cx = 0, cy = 0) {\n        const currentValues = this._getValues(touches, cx, cy);\n        let deltaLogScale = Math.log2(currentValues[2] / this.lastValues[2]);\n        if (Number.isNaN(deltaLogScale) || !Number.isFinite(deltaLogScale)) {\n            deltaLogScale = 0;\n        }\n        const oldLogScale = this.logScale;\n        this.logScale += deltaLogScale;\n        if (this.logScale <= this.logMinScale) this.logScale = this.logMinScale;\n        if (this.logScale >= this.logMaxScale) this.logScale = this.logMaxScale;\n\n        const deltaScale = Math.pow(2, this.logScale - oldLogScale);\n\n        this.dx = (this.dx - this.lastValues[0]) * deltaScale + currentValues[0];\n        this.dy = (this.dy - this.lastValues[1]) * deltaScale + currentValues[1];\n\n        this.lastValues = currentValues;\n    }\n\n    /**\n     * @param {number} amount\n     **/\n    zoom(amount, cx = 0, cy = 0) {\n        const oldLogScale = this.logScale;\n        this.logScale += Math.log2(amount);\n        if (this.logScale <= this.logMinScale) this.logScale = this.logMinScale;\n        if (this.logScale >= this.logMaxScale) this.logScale = this.logMaxScale;\n\n        const deltaScale = Math.pow(2, this.logScale - oldLogScale);\n\n        this.dx = (this.dx - cx) * deltaScale + cx;\n        this.dy = (this.dy - cy) * deltaScale + cy;\n    }\n\n    /**\n     * @param {Map<number, Touch>} touches\n     **/\n    update(touches, cx = 0, cy = 0) {\n        this.lastValues = this._getValues(touches, cx, cy);\n    }\n}\n\n/**\n * An immutable view of `Pannable`, returned by `Pannable::getState`.\n **/\nexport class PannableState {\n    /**\n     * @param {number} dx\n     * @param {number} dy\n     * @param {number} logScale\n     **/\n    constructor(dx, dy, logScale) {\n        /** @readonly The X offset to move coordinates by *after* multiplying them by `scale` **/\n        this.dx = dx;\n        /** @readonly The Y offset to move coordinates by *after* multiplying them by `scale` **/\n        this.dy = dy;\n        /** @readonly The base-2 logarithm of `scale`, used internally for precision purposes **/\n        this.logScale = logScale;\n        /** @readonly The zoom strength **/\n        this.scale = Math.pow(2, logScale);\n    }\n\n    /**\n     * Transforms the input coordinates from the \"pannable\" coordinates\n     * to the \"real\" coordinates (what you will display on screen)\n     *\n     * @param {number} x The X \"pannable\" coordinate\n     * @param {number} y The Y \"pannable\" coordinate\n     * @returns {[x: number, y: number]}\n     **/\n    get(x, y) {\n        return [x * this.scale + this.dx, y * this.scale + this.dy];\n    }\n\n    /**\n     * Transforms the input coordinates from the \"real\" coordinates (where they are on screen)\n     * to the \"pannable\" coordinates; this is the inverse operation of the `PannableState::get` method.\n     *\n     * @param {number} x The X \"real\" coordinate\n     * @param {number} y The Y \"real\" coordinate\n     * @returns {[x: number, y: number]}\n     **/\n    inverse(x, y) {\n        return [(x - this.dx) / this.scale, (y - this.dy) / this.scale];\n    }\n}\n\n/**\n * @typedef {PannableOptions & {\n *   onUpdate?: (state: PannableState) => void,\n *   wheelSensitivity?: number,\n *   emSize?: number,\n *   pageSize?: number,\n * }} AttachPannableOptions\n **/\n\n/**\n * A simple wrapper around `Pannable`, which handles touch input and scrollwheel input\n * @param {HTMLElement} element\n * @param {AttachPannableOptions} options\n **/\nexport function attachPannable(element, options) {\n    const pannable = new Pannable(options);\n    const wheelSensitivity = options?.wheelSensitivity ?? 0.005;\n    const emSize = options?.emSize ?? 12;\n    const pageSize = options?.pageSize ?? options?.emSize ?? 12;\n    setTimeout(() => options?.onUpdate?.(pannable.getState()), 0);\n\n    const detachTouch = attachTouch(element, {\n        downscale: options.downscale,\n        preventDefault: true,\n        onDown(_, touches) {\n            pannable.update(touches);\n        },\n        onUp(_, touches) {\n            pannable.update(touches);\n        },\n        onMove(_, touches) {\n            pannable.move(touches);\n            options?.onUpdate?.(pannable.getState());\n        }\n    });\n\n    const wheelListener = element.addEventListener(\"wheel\", (event) => {\n        const dpr = window.devicePixelRatio || 1;\n        const bounding = element.getBoundingClientRect();\n\n        let deltaY = -event.deltaY;\n        if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) {\n            deltaY *= emSize;\n        } else if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n            deltaY *= pageSize;\n        }\n\n        pannable.zoom(\n            Math.pow(2, deltaY * wheelSensitivity * dpr),\n            (event.clientX - bounding.x) * dpr,\n            (event.clientY - bounding.y) * dpr\n        );\n\n        options?.onUpdate?.(pannable.getState());\n    });\n\n    return function detachPannable() {\n        element.removeEventListener(wheelListener);\n        detachTouch();\n    };\n}\n\nexport const StrokeMode = Object.freeze({\n    DEFAULT: 0,\n    INSIDE: 1,\n    OUTSIDE: 2,\n});\n\n/**\n * @typedef {typeof StrokeMode[keyof typeof StrokeMode]} StrokeMode\n **/\n\nexport class PixelPerfectContext2D {\n    constructor(canvas) {\n        /** @readonly **/\n        this.canvas = canvas;\n\n        /** @readonly **/\n        this.context = canvas.getContext(\"2d\");\n        if (!this.context) throw new Error(\"Couldn't get 2d canvas context\");\n\n        this.context.imageSmoothingEnabled = false;\n\n        /** @private **/\n        this._scaleX = this.context.getTransform().m11;\n\n        /** @private **/\n        this._scaleY = this.context.getTransform().m22;\n\n        /** @private **/\n        this._lineWidth = this.context.lineWidth;\n    }\n\n    /**\n     * @returns {string}\n     **/\n    get fillStyle() {\n        return this.context.fillStyle;\n    }\n\n    /**\n     * @param {string} value\n     **/\n    set fillStyle(value) {\n        this.context.fillStyle = value;\n    }\n\n    /**\n     * @returns {string}\n     **/\n    get strokeStyle() {\n        return this.context.strokeStyle;\n    }\n\n    /**\n     * @param {string} value\n     **/\n    set strokeStyle(value) {\n        this.context.strokeStyle = value;\n    }\n\n    get lineWidth() {\n        return this.context.lineWidth;\n    }\n\n    /**\n     * @param {number} value - Line width\n     **/\n    set lineWidth(value) {\n        this.context.lineWidth = value;\n    }\n\n    /**\n     * Returns an adjustment parameter to ensure that functions like fillRect, strokeRect\n     * are pixel-perfect.\n     *\n     * Given a drawing function `f` that offsets `x` or `y` by `lineWidth / 2`,\n     * this function returns `delta` such that `f(x + delta, y + delta)` is pixel-perfect.\n     *\n     * @param {StrokeMode} mode\n     * @returns {number} The adjustment parameter\n     **/\n    lineWidthDelta(mode = StrokeMode.DEFAULT) {\n        if (mode === StrokeMode.INSIDE) {\n            return [\n                this.context.lineWidth / 2 * this._scaleX,\n                this.context.lineWidth / 2 * this._scaleY\n            ];\n        } else if (mode === StrokeMode.OUTSIDE) {\n            return [\n                this.context.lineWidth / -2 * this._scaleX,\n                this.context.lineWidth / -2 * this._scaleY\n            ];\n        } else {\n            return [\n                floatingPart(this.context.lineWidth / 2 * this._scaleX),\n                floatingPart(this.context.lineWidth / 2 * this._scaleY)\n            ];\n        }\n    }\n\n    get perfectLineWidth() {\n        const lwx = Math.round(this.context.lineWidth * this._scaleX) / this._scaleX;\n        const lwy = Math.round(this.context.lineWidth * this._scaleY) / this._scaleY;\n\n        if (this._scaleX === this._scaleY) return lwx;\n\n        // Heuristic: choose the direction that minimizes `subpixelPenalty(lineWidth)`\n        // on the opposite direction\n        if (subpixelPenalty(lwx * this._scaleY) < subpixelPenalty(lwy * this._scaleX)) {\n            return lwx;\n        } else {\n            return lwy;\n        }\n    }\n\n    get lineCap() {\n        return this.context.lineCap;\n    }\n\n    /**\n     * @param {\"butt\" | \"square\"} value\n     **/\n    set lineCap(value) {\n        if (value !== \"butt\" && value !== \"square\") {\n            console.warn(\"PixelPerfectContext2D::lineCap only supports 'butt' and 'square' modes\");\n            return;\n        }\n\n        this.context.lineCap = value;\n    }\n\n    /**\n     * Similar to `lineWidthDelta`, but for line endings: reads the value of `lineCap` and returns\n     * a value to offset the line by so that it appears pixel-perfect.\n     **/\n    lineCapDelta() {\n        if (this.context.lineCap === \"butt\") return [0, 0];\n        else return this.lineWidthDelta();\n    }\n\n    /**\n     * Scales the canvas operations up/down. Calling `scale` twice will stack the scale operations together.\n     *\n     * Calling `scale` on `this.context` is not supported.\n     *\n     * @param {number} scaleX\n     * @param {number} scaleY\n     **/\n    scale(scaleX, scaleY) {\n        this._scaleX *= scaleX;\n        this._scaleY *= scaleY;\n        this.context.scale(scaleX, scaleY);\n    }\n\n    /**\n     * Resets the transforms (mainly the `scale()` operation)\n     **/\n    resetTransform() {\n        this._scaleX = 1;\n        this._scaleY = 1;\n        this.context.resetTransform();\n    }\n\n    /** @private **/\n    _getX(x) {\n        return Math.round(x * this._scaleX);\n    }\n\n    /** @private **/\n    _getY(y) {\n        return Math.round(y * this._scaleY);\n    }\n\n    /** @private **/\n    _unscaleX(x) {\n        return x / this._scaleX;\n    }\n\n    /** @private **/\n    _unscaleY(y) {\n        return y / this._scaleY;\n    }\n\n    clearRect(x, y, width, height) {\n        const x1 = this._getX(x);\n        const y1 = this._getY(y);\n        const x2 = this._getX(x + width);\n        const y2 = this._getY(y + height);\n\n        if (\n            x === 0 && y === 0 && approxEq(x2, this.canvas.width) && approxEq(y2, this.canvas.height)\n        ) {\n            this.context.clearRect(\n                0,\n                0,\n                this._unscaleX(this.canvas.width) + 1,\n                this._unscaleY(this.canvas.height) + 1\n            );\n        } else {\n            this.context.fillRect(\n                this._unscaleX(x1),\n                this._unscaleY(y1),\n                this._unscaleX(x2 - x1),\n                this._unscaleY(y2 - y1)\n            );\n        }\n    }\n\n    /**\n     * Fills a pixel-perfect rectangle; `x`, `y`, `width` and `height` are rounded so that drawing\n     * two rectangles at `(x, y)` and `(x + width, y + height)` would have their corners adjacent.\n     *\n     * @param {number} x - The upper-left corner's X coordinate, rounded\n     * @param {number} y - The upper-left corner's Y coordinate, rounded\n     * @param {number} width - The width of the rectangle\n     * @param {number} height - The height of the rectangle\n     **/\n    fillRect(x, y, width, height) {\n        const x1 = this._getX(x);\n        const y1 = this._getY(y);\n        const x2 = this._getX(x + width);\n        const y2 = this._getY(y + height);\n\n        this.context.fillRect(\n            this._unscaleX(x1),\n            this._unscaleY(y1),\n            this._unscaleX(x2 - x1),\n            this._unscaleY(y2 - y1)\n        );\n    }\n\n    /**\n     * Strokes a pixel-perfect rectangle; `x`, `y`, `width` and `height` are rounded so that drawing\n     * two rectangles at `(x, y)` and `(x + width, y + height)` would have their corners\n     * (not accounting for the line width) adjacent.\n     *\n     * @param {number} x - The upper-left corner's X coordinate, rounded\n     * @param {number} y - The upper-left corner's Y coordinate, rounded\n     * @param {number} width - The width of the rectangle\n     * @param {number} height - The height of the rectangle\n     * @param {StrokeMode=} mode - If `StrokeMode.DEFAULT` (default), then behaves like the vanilla\n     *   `strokeRect`: a `lineWidth` of 2 would draw 1 pixel outside and 1 pixel inside the\n     *   `(x, y, x + width, y + height)` base rectangle.\n     *   If `StrokeMode.INSIDE`, then all of the lineWidth will be inside the base rectangle.\n     *   If `StrokeMode.OUTSIDE`, then all of the lineWidth will be outside the base rectangle.\n     **/\n    strokeRect(x, y, width, height, mode = StrokeMode.DEFAULT) {\n        const old_lw = this.context.lineWidth;\n        this.context.lineWidth = this.perfectLineWidth;\n\n        const [dx, dy] = this.lineWidthDelta(mode);\n        const x1 = this._getX(x);\n        const y1 = this._getY(y);\n        const x2 = this._getX(x + width);\n        const y2 = this._getY(y + height);\n\n        this.context.strokeRect(\n            this._unscaleX(x1 + dx),\n            this._unscaleY(y1 + dy),\n            this._unscaleX(x2 - x1 - dx * 2),\n            this._unscaleY(y2 - y1 - dy * 2)\n        );\n\n        this.context.lineWidth = old_lw;\n    }\n\n    /**\n     * @param {number} x - Origin X coordinate\n     * @param {number} y - Origin Y coordinate\n     * @param {number} length - The vertical length of the line, in pixels\n     **/\n    verticalLine(x, y, length) {\n        const old_lw = this.context.lineWidth;\n        this.context.lineWidth = this.perfectLineWidth;\n\n        this.context.beginPath();\n        const [dx] = this.lineWidthDelta();\n        const [, dy] = this.lineCapDelta();\n\n        const x1 = this._getX(x);\n        const y1 = this._getY(y);\n        const y2 = this._getY(y + length);\n\n        if (y2 < y1) {\n            this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y2 + dy));\n            this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y1 - dy) - 1);\n        } else {\n            this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y1 + dy));\n            this.context.lineTo(this._unscaleX(x1 + dx), this._unscaleY(y2 - dy) + 1);\n        }\n\n        this.context.stroke();\n\n        this.context.lineWidth = old_lw;\n    }\n\n    /**\n     * @param {number} x - Origin X coordinate\n     * @param {number} y - Origin Y coordinate\n     * @param {number} length - The horizontal length of the line, in pixels\n     **/\n     horizontalLine(x, y, length) {\n        const old_lw = this.context.lineWidth;\n        this.context.lineWidth = this.perfectLineWidth;\n\n        this.context.beginPath();\n\n        const [dx] = this.lineCapDelta();\n        const [, dy] = this.lineWidthDelta();\n\n        if (length < 0) {\n            x = x + length;\n            length = -length;\n        }\n\n        const x1 = this._getX(x);\n        const y1 = this._getY(y);\n        const x2 = this._getX(x + length);\n\n        if (x2 < x1) {\n            this.context.moveTo(this._unscaleX(x2 + dx), this._unscaleY(y1 + dy));\n            this.context.lineTo(this._unscaleX(x1 - dx) - 1, this._unscaleY(y1 + dy));\n        } else {\n            this.context.moveTo(this._unscaleX(x1 + dx), this._unscaleY(y1 + dy));\n            this.context.lineTo(this._unscaleX(x2 - dx) + 1, this._unscaleY(y1 + dy));\n        }\n\n        this.context.stroke();\n\n        this.context.lineWidth = old_lw;\n    }\n}\n\nfunction isInteger(value, epsilon = 0) {\n    return Math.abs(Math.round(value) - value) <= epsilon;\n}\n\nfunction floatingPart(value) {\n    return value - Math.floor(value);\n}\n\nfunction subpixelPenalty(value) {\n    return 1 - Math.abs(floatingPart(value) - 0.5);\n}\n\nfunction approxEq(a, b, epsilon = 0) {\n    return Math.abs(a - b) <= epsilon;\n}\n\n/**\n * Performs a shallow clone on `map.get(key)`, and replaces that value in the map.\n * @template K,V\n * @param {Map<K, V>} map - The map to get the object from, will be mutated\n * @param {K} key\n * @returns {V | undefined}\n **/\nfunction cloneGet(map, key) {\n    let value = map.get(key);\n    if (!value) return value;\n    if (typeof value === 'object') {\n        value = {\n            ...value\n        };\n        map.set(key, value);\n    }\n\n    return value;\n}\n"],"names":["MAX_CANVAS_SIZE","attachCanvas","canvas","options","container","onResize","downscale","old_width","old_height","old_dpr","resize","dpr","width","height","isInteger","unbindDpr","listenPixelRatio","resizeObserver","entries","element","callback","fireFirst","first","media","update","getContext2D","PixelPerfectContext2D","attachTouch","touches","queued","queuedTouches","get_dpr","getCoords","bounding","event","onDown","x","y","touch","onMove","cloneGet","onUp","onCancel","onTouchMove","Pannable","cx","cy","mx","my","mx2","my2","n","sx","sy","PannableState","currentValues","deltaLogScale","oldLogScale","deltaScale","amount","dx","dy","logScale","attachPannable","pannable","wheelSensitivity","emSize","pageSize","detachTouch","_","wheelListener","deltaY","StrokeMode","value","mode","floatingPart","lwx","lwy","subpixelPenalty","scaleX","scaleY","x1","y1","x2","y2","approxEq","old_lw","length","epsilon","a","b","map","key"],"mappings":"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"}