Men的博客

欢迎光临!

0%

点、线、面绘制

/**
 * 绘制点
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number} screenX - 屏幕坐标X
 * @param {number} screenY - 屏幕坐标Y
 */
draw(ctx: CanvasRenderingContext2D, screenX, screenY) {
    ctx.save();
    ctx.strokeStyle = this.strokeStyle;
    ctx.fillStyle = this.fillStyle;
    ctx.lineWidth = this.lineWidth;
    ctx.beginPath(); //Start path
    //keep size
    //地理坐标 转回 屏幕坐标
    ctx.setTransform(1,0,0,1,0,0);
    ctx.arc(screenX, screenY, this.radius, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.stroke();
    ctx.restore();
}
/**
 * 绘制线
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number[][]} screen - 线对应坐标点的屏幕坐标集合
 */
draw(ctx: CanvasRenderingContext2D, screen: number[][]) {
    ctx.save();
    ctx.strokeStyle = this.strokeStyle;
    ctx.lineWidth = this.lineWidth;
    //keep lineWidth
    ctx.setTransform(1,0,0,1,0,0);
    ctx.beginPath();
    screen.forEach( (point: any,index) => {
        const screenX = point[0], screenY = point[1];
        if (index === 0){
            ctx.moveTo(screenX, screenY);
        } else {
            ctx.lineTo(screenX, screenY);
        }
    });
    ctx.stroke();
    ctx.restore();
}
 /**
 * 绘制面
 * @remarks
 * 奇偶填充
 * https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number[][][]} screen - 面对应坐标点的屏幕坐标集合
 */
draw(ctx: CanvasRenderingContext2D, screen: number[][][]) {
    ctx.save();
    ctx.strokeStyle = this.strokeStyle;
    ctx.fillStyle = this.fillStyle;
    ctx.lineWidth = this.lineWidth;
    //keep lineWidth
    ctx.setTransform(1,0,0,1,0,0);
    //TODO:  exceeding the maximum extent(bound), best way is overlap by extent. find out: maximum is [-PI*R, PI*R]??
    ctx.beginPath();
    screen.forEach( ring => {
        ring.forEach((point: any,index) => {
            const screenX = point[0], screenY = point[1];
            if (index === 0){
                ctx.moveTo(screenX, screenY);
            } else {
                ctx.lineTo(screenX, screenY);
            }
        });
    });
    ctx.closePath();
    //奇偶填充
    //https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill
    ctx.fill("evenodd");
    ctx.stroke();
    ctx.restore();
}
 /**
 * 绘制图标
 * @remarks
 * 注意异步加载
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number} screenX - 屏幕坐标X
 * @param {number} screenY - 屏幕坐标Y
 */
async draw(ctx: CanvasRenderingContext2D, screenX, screenY) {
    if (!this.loaded) await this.load();
    if (this.icon) {
        ctx.save();
        const matrix = (ctx as any).getTransform();
        //keep size
        ctx.setTransform(1,0,0,1,0,0);
        //请对应参考offset属性的描述内容
        ctx.drawImage(this.icon, screenX + this.offsetX, screenY + this.offsetY, this.width, this.height);
        ctx.restore();
    }
}
/**
 * 绘制字符符号
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number} screenX - 屏幕坐标X
 * @param {number} screenY - 屏幕坐标Y
 */
draw(ctx: CanvasRenderingContext2D, screenX, screenY) {
    ctx.save();
    ctx.strokeStyle = this.strokeStyle;
    ctx.fillStyle = this.fillStyle;
    ctx.lineWidth = this.lineWidth;
    ctx.beginPath(); //Start path
    //keep size
    ctx.setTransform(1,0,0,1,0,0);
    //绘制外圈
    ctx.arc(screenX, screenY, this.radius, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.stroke();
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.fillStyle = this.fontColor;
    ctx.font =  this.fontSize + "px/1 " + this.fontFamily +  " " + this.fontWeight;
    //绘制字符
    ctx.fillText(this.letter, screenX, screenY);
    ctx.restore();
}
/**
 * 绘制箭头
 * @param {CanvasRenderingContext2D} ctx - 绘图上下文
 * @param {number[][]} screen - 线对应坐标点的屏幕坐标集合
 */
draw(ctx: CanvasRenderingContext2D, screen: number[][]) {
    ctx.save();
    ctx.strokeStyle = this.strokeStyle;
    ctx.lineWidth = this.lineWidth;
    //keep lineWidth
    ctx.setTransform(1,0,0,1,0,0);
    ctx.beginPath();
    screen.forEach( (point: any,index) => {
        const screenX = point[0], screenY = point[1];
        if (index === 0){
            ctx.moveTo(screenX, screenY);
        } else {
            ctx.lineTo(screenX, screenY);
        }
    });
    ctx.stroke();
    //已知 起点和终点  求沿线距起点定长的点
    const _getPointAlongLine = (p1, p2, d) => {
        //line length
        let l = Math.sqrt((p2[0] - p1[0]) * (p2[0] - p1[0]) + (p2[1] - p1[1]) * (p2[1] - p1[1]));
        let t = d / l;
        return [(1 - t) * p1[0] + t * p2[0], (1 - t) * p1[1] + t * p2[1]];
    };
    //已知 起点 y = kx + b   求沿线距起点定长的点 两个点
    const _getPointAlongLine2 = (k, b, p, d) => {
        let x0 = p[0] + Math.sqrt( (d * d) / (k * k + 1)), x1 = p[0] - Math.sqrt( (d * d) / (k * k + 1));
        return [[x0, k * x0 + b], [x1, k * x1 + b]];
    };
    screen.reduce( (prev, cur) => {
        if (prev) {
            const length = Math.sqrt((cur[0] - prev[0]) * (cur[0] - prev[0]) + (cur[1] - prev[1]) * (cur[1] - prev[1]));
            if (length >= this.minLength) {
                //中点 即箭头
                const [middleX, middleY] = [(prev[0] + cur[0])/2, (prev[1] + cur[1])/2];
                //箭尾垂线的垂足
                const [footX, footY] = _getPointAlongLine([middleX, middleY], prev, Math.cos(this.arrowAngle) * this.arrowLength);
                const k = (cur[1] - prev[1]) / (cur[0] - prev[0]);
                // 1/k 垂线
                const points = _getPointAlongLine2( -1/k, footY - footX * -1/k, [footX, footY], Math.sin(this.arrowAngle) * this.arrowLength);
                //两点
                points.forEach(point => {
                    ctx.beginPath();
                    ctx.moveTo(middleX, middleY);
                    ctx.lineTo(point[0], point[1]);
                    ctx.stroke();
                });
            }
            return cur;
        } else {
            return cur;
        }
    });
    ctx.restore();
}