# src/layers/canvasUtil.js

    function drawDashLine(ctx, line) {
        if (ctx instanceof CanvasRenderingContext2D && line instanceof Array) {
            ctx.setLineDash([10, 5]);
            ctx.strokeStyle = 'rgba(0,252,100,0.6)';
            ctx.lineWidth = 6;

            ctx.beginPath();
            ctx.moveTo(line[0][0], line[0][1]);
            for (let i = 1; i < line.length; i += 1) {
                ctx.lineTo(line[i][0], line[i][1]);
            }
            ctx.stroke();
            ctx.closePath();
        }
    }

    function drawGradientLine(ctx, line, shadow = false) {
        if (ctx instanceof CanvasRenderingContext2D && line instanceof Array) {
            // build gradient style
            var gradient = ctx.createLinearGradient(0, 0, 600, 0);
            gradient.addColorStop(0, "rgba(0,255,100,0.9)");
            gradient.addColorStop(1, "rgba(255,255,255,0.1)");

            if (shadow) {
                console.warn(`enabling line shadowBlur`);
                ctx.shadowBlur = 4;
                ctx.shadowColor = '#0f0';
            }

            ctx.strokeStyle = gradient;
            ctx.setLineDash([]);
            ctx.globalAlpha = 0.9;
            ctx.globalCompositeOperation = 'source-over';

            // ctx.strokeStyle = 'green';
            ctx.lineCap = "round";  // square
            ctx.lineJoin = 'round'; // bevel
            ctx.lineWidth = 6;

            ctx.beginPath();
            ctx.moveTo(line[0][0], line[0][1]);
            for (let i = 1; i < line.length; i += 1) {
                ctx.lineTo(line[i][0], line[i][1]);
            }
            ctx.stroke();
            ctx.closePath();
        }
    }

    function drawArrowLine(ctx, line, enableAni = false) {
        if (ctx instanceof CanvasRenderingContext2D && line instanceof Array) {

                // ctx.fillRect(0, 0, 600, 400);
                setLineStyle(ctx);

                ctx.beginPath();
                ctx.moveTo(line[0][0], line[0][1]);
                for (let i = 1; i < line.length; i += 1) {
                    ctx.lineTo(line[i][0], line[i][1]);
                }
                ctx.stroke();
                ctx.closePath();

            function drawArrows() {
                let aniOffset = .2;
                aniOffset = aniOffset < 1 ? aniOffset + .01 : .2;
                for (let i = 1; i < line.length; i += 1) {
                    drawArrow(ctx, line[i-1], line[i], aniOffset); // one segment.
                }
                if (enableAni) requestAnimationFrame(drawArrows);
            }
            drawArrows();
        }
    }

    function setLineStyle(ctx, shadow = false) {
        ctx.strokeStyle = 'green';
        ctx.setLineDash([]);
        ctx.globalAlpha = 0.95;
        ctx.globalCompositeOperation = 'source-over';
        if (shadow) {
            console.warn(`enabling line shadowBlur`);
            ctx.shadowBlur = 4;
            ctx.shadowColor = '#0f0';
        } else {
            ctx.shadowBlur = 0;
        }

        // ctx.strokeStyle = 'green';
        ctx.lineCap = "round";  // square
        ctx.lineJoin = 'round'; // bevel
        ctx.lineWidth = 12;
    }

    function drawArrow(ctx, startPoint, endPoint, aniOffset) {
        ctx.beginPath();
        ctx.strokeStyle = 'white';
        ctx.lineWidth = 2;

        // arrow img: assets/up.png
        var points = generatePoints(startPoint, endPoint, 30, ctx, aniOffset);
        // console.warn(`draw segment arrows done... from ${startPoint.join()} to ${endPoint.join()}`);
        // // calc left/right point.
        // var lrPoints = calcLRPoints(point, rotate);

        ctx.closePath();
    }

    function generatePoints(startP, endP, stepSize = 20, ctx, aniOffset = 0.5) {
        const radA = Math.atan((endP[1] - startP[1]) / (endP[0] - startP[0]));  // k should be transform to a in Rad.
        const dist = calcDist(startP, endP);
        let points = [];
        const steps = dist / stepSize;

        const drawImg = (pX, pY) => {
            if (img && ctx) {
                ctx.save();
                // ctx.beginPath();
                ctx.translate(pX , pY);  // consider img position and imgWidth/Height.
                const rotateAng = 180 * radA / Math.PI;
                ctx.rotate(radA);
                // ctx.arc(0, 0, 2, 0, 2 * Math.PI);
                ctx.drawImage(img, -img.width / 2,  -img.width/2);
                // ctx.stroke();
                console.warn(`drawIcon at ${pX}, ${pY}, with angle rad: ${radA}`);
                // ctx.closePath();
                ctx.restore();
            }
        }

        // gen points by stepSize.. if enable corner arrow, start s with (0~1) float number.
        for (let s = aniOffset; s <= steps; s += 1) {
            const pX = startP[0] + s * stepSize * Math.cos(radA);
            const pY = startP[1] + s * stepSize * Math.sin(radA);
            points.push([pX, pY]);
            if (ctx) {
                var img = new Image();
                img.src = '../../assets/arrow.png';
                img.onload = function() {
                    drawImg(pX, pY);
                }
            }
        }
        return points;
    }

    function calcDist(startP, endP) {
        return Math.sqrt((endP[1] - startP[1]) ** 2 + (endP[0] - startP[0]) ** 2);
    }

    /**
     *      . <-- center coordinate [x, y].
     *     / \
     *    `   `
     *
     */
    function calcLRPoints(center, rotate) {
        const arrawRadius = 3;
        return center;
    }

    function drawGradientRect(ctx, line) {

    }