feat: change curve arrow point to center
This commit is contained in:
parent
b892a9a76f
commit
c50cdd9b6f
@ -798,7 +798,6 @@ function drawLine(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const { x: dx2 = 0, y: dy2 = 0 } = c2 ?? {};
|
||||
const [c1x, c1y] = [x1 + dx1 * s, y1 + dy1 * s];
|
||||
const [c2x, c2y] = [x2 + dx2 * s, y2 + dy2 * s];
|
||||
const t = direction < 0 ? 0.55 : 0.45;
|
||||
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
@ -829,51 +828,30 @@ function drawLine(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
ctx.beginPath();
|
||||
ctx.setLineDash([0]);
|
||||
|
||||
let [ax, ay] = [0, 0];
|
||||
let r = 0;
|
||||
switch (type) {
|
||||
case MapRouteType.直线:
|
||||
{
|
||||
ax = x1 + (x2 - x1) * t;
|
||||
ay = y1 + (y2 - y1) * t;
|
||||
r = Math.atan2(y2 - y1, x2 - x1);
|
||||
const { dx, dy, r } = (() => {
|
||||
switch (type) {
|
||||
case MapRouteType.直线: {
|
||||
const t = direction < 0 ? 0.55 : 0.45;
|
||||
const dx = x1 + (x2 - x1) * t;
|
||||
const dy = y1 + (y2 - y1) * t;
|
||||
const r = Math.atan2(y2 - y1, x2 - x1) + (direction > 0 ? Math.PI : 0);
|
||||
return { dx, dy, r };
|
||||
}
|
||||
break;
|
||||
case MapRouteType.二阶贝塞尔曲线:
|
||||
{
|
||||
const t1x = x1 + (c1x - x1) * t;
|
||||
const t1y = y1 + (c1y - y1) * t;
|
||||
const t2x = c1x + (x2 - c1x) * t;
|
||||
const t2y = c1y + (y2 - c1y) * t;
|
||||
ax = t1x + (t2x - t1x) * t;
|
||||
ay = t1y + (t2y - t1y) * t;
|
||||
r = Math.atan2(t2y - t1y, t2x - t1x);
|
||||
case MapRouteType.二阶贝塞尔曲线: {
|
||||
const { x: dx, y: dy, t } = getBezier2Center(p1, { x: c1x, y: c1y }, p2);
|
||||
const r = getBezier2Tange(p1, { x: c1x, y: c1y }, p2, t) + (direction > 0 ? Math.PI : 0);
|
||||
return { dx, dy, r };
|
||||
}
|
||||
break;
|
||||
case MapRouteType.三阶贝塞尔曲线:
|
||||
{
|
||||
const t1x = x1 + (c1x - x1) * t;
|
||||
const t1y = y1 + (c1y - y1) * t;
|
||||
const t2x = c1x + (c2x - c1x) * t;
|
||||
const t2y = c1y + (c2y - c1y) * t;
|
||||
const t3x = c2x + (x2 - c2x) * t;
|
||||
const t3y = c2y + (y2 - c2y) * t;
|
||||
const t12x = t1x + (t2x - t1x) * t;
|
||||
const t12y = t1y + (t2y - t1y) * t;
|
||||
const t23x = t2x + (t3x - t2x) * t;
|
||||
const t23y = t2y + (t3y - t2y) * t;
|
||||
ax = t12x + (t23x - t12x) * t;
|
||||
ay = t12y + (t23y - t12y) * t;
|
||||
r = Math.atan2(t23y - t12y, t23x - t12x);
|
||||
case MapRouteType.三阶贝塞尔曲线: {
|
||||
const { x: dx, y: dy, t } = getBezier3Center(p1, { x: c1x, y: c1y }, { x: c2x, y: c2y }, p2);
|
||||
const r = getBezier3Tange(p1, { x: c1x, y: c1y }, { x: c2x, y: c2y }, p2, t) + (direction > 0 ? Math.PI : 0);
|
||||
return { dx, dy, r };
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ctx.translate(ax, ay);
|
||||
if (direction > 0) {
|
||||
r += Math.PI;
|
||||
}
|
||||
default:
|
||||
return { dx: 0, dy: 0, r: 0 };
|
||||
}
|
||||
})();
|
||||
ctx.translate(dx, dy);
|
||||
ctx.moveTo(Math.cos(r + Math.PI / 5) * s * 10, Math.sin(r + Math.PI / 5) * s * 10);
|
||||
ctx.lineTo(0, 0);
|
||||
ctx.lineTo(Math.cos(r - Math.PI / 5) * s * 10, Math.sin(r - Math.PI / 5) * s * 10);
|
||||
@ -967,3 +945,71 @@ function drawRobot(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
ctx.restore();
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 辅助函数
|
||||
function getBezier2Center(p1: Point, c1: Point, p2: Point): Point & { t: number } {
|
||||
const fn = (t: number) => {
|
||||
const x = (1 - t) ** 2 * p1.x + 2 * (1 - t) * t * c1.x + t ** 2 * p2.x;
|
||||
const y = (1 - t) ** 2 * p1.y + 2 * (1 - t) * t * c1.y + t ** 2 * p2.y;
|
||||
return { x, y };
|
||||
};
|
||||
return calcBezierCenter(fn);
|
||||
}
|
||||
function getBezier2Tange(p1: Point, c1: Point, p2: Point, t: number): number {
|
||||
const dx = 2 * (1 - t) * (c1.x - p1.x) + 2 * t * (p2.x - c1.x);
|
||||
const dy = 2 * (1 - t) * (c1.y - p1.y) + 2 * t * (p2.y - c1.y);
|
||||
return Math.atan2(dy, dx);
|
||||
}
|
||||
|
||||
function getBezier3Center(p1: Point, c1: Point, c2: Point, p2: Point): Point & { t: number } {
|
||||
const fn = (t: number) => {
|
||||
const x = (1 - t) ** 3 * p1.x + 3 * (1 - t) ** 2 * t * c1.x + 3 * (1 - t) * t ** 2 * c2.x + t ** 3 * p2.x;
|
||||
const y = (1 - t) ** 3 * p1.y + 3 * (1 - t) ** 2 * t * c1.y + 3 * (1 - t) * t ** 2 * c2.y + t ** 3 * p2.y;
|
||||
return { x, y };
|
||||
};
|
||||
return calcBezierCenter(fn);
|
||||
}
|
||||
function getBezier3Tange(p1: Point, c1: Point, c2: Point, p2: Point, t: number): number {
|
||||
const t1 = 3 * Math.pow(1 - t, 2);
|
||||
const t2 = 6 * (1 - t) * t;
|
||||
const t3 = 3 * Math.pow(t, 2);
|
||||
|
||||
const dx = t1 * (c1.x - p1.x) + t2 * (c2.x - c1.x) + t3 * (p2.x - c2.x);
|
||||
const dy = t1 * (c1.y - p1.y) + t2 * (c2.y - c1.y) + t3 * (p2.y - c2.y);
|
||||
return Math.atan2(dy, dx);
|
||||
}
|
||||
|
||||
function calcBezierCenter(bezierFn: (t: number) => Point): Point & { t: number } {
|
||||
const count = 23;
|
||||
|
||||
let length = 0;
|
||||
let temp = bezierFn(0);
|
||||
const samples = Array.from({ length: count }, (_, i) => {
|
||||
const t = (i + 1) / count;
|
||||
const point = bezierFn(t);
|
||||
const dx = point.x - temp.x;
|
||||
const dy = point.y - temp.y;
|
||||
length += Math.sqrt(dx * dx + dy * dy);
|
||||
temp = point;
|
||||
return { ...point, t };
|
||||
});
|
||||
|
||||
const target = length / 2;
|
||||
let accumulated = 0;
|
||||
for (let i = 0; i < samples.length - 1; i++) {
|
||||
const p1 = samples[i];
|
||||
const p2 = samples[i + 1];
|
||||
const segment = Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
|
||||
if (accumulated + segment >= target) {
|
||||
const ratio = (target - accumulated) / segment;
|
||||
return {
|
||||
x: p1.x + (p2.x - p1.x) * ratio,
|
||||
y: p1.y + (p2.y - p1.y) * ratio,
|
||||
t: p1.t + ratio * (p2.t - p1.t),
|
||||
};
|
||||
}
|
||||
accumulated += segment;
|
||||
}
|
||||
return samples[samples.length - 1];
|
||||
}
|
||||
//#endregion
|
||||
|
Loading…
x
Reference in New Issue
Block a user