/**
* 绘制点
* @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();
}