fix: 新增态 效果
This commit is contained in:
106
src/utils/defenseLine.ts
Normal file
106
src/utils/defenseLine.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 战场防御线:反弓曲线路径 + 锯齿齿形
|
||||
*/
|
||||
|
||||
export type LngLat = [number, number]
|
||||
|
||||
/** 二次贝塞尔 B(t) = (1-t)²P0 + 2(1-t)t P1 + t² P2 */
|
||||
function quadraticBezier(P0: LngLat, P1: LngLat, P2: LngLat, t: number): LngLat {
|
||||
const u = 1 - t
|
||||
return [
|
||||
u * u * P0[0] + 2 * u * t * P1[0] + t * t * P2[0],
|
||||
u * u * P0[1] + 2 * u * t * P1[1] + t * t * P2[1],
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成反弓曲线路径:通过 path 各点,弧面向进攻方向(西侧)凸出
|
||||
* @param path 关键点 [[lng, lat], ...](如大不里士→萨南达季→克尔曼沙赫)
|
||||
* @param bulgeWest 向西凸出量(经度,正值表示弧顶在西侧/面向进攻方)
|
||||
* @param samplesPerSegment 每段贝塞尔采样点数
|
||||
*/
|
||||
export function createCurvedDefensePath(
|
||||
path: LngLat[],
|
||||
bulgeWest: number = 0.35,
|
||||
samplesPerSegment: number = 24
|
||||
): LngLat[] {
|
||||
if (path.length < 2) return path
|
||||
const out: LngLat[] = []
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const a = path[i]
|
||||
const b = path[i + 1]
|
||||
const midLng = (a[0] + b[0]) / 2
|
||||
const midLat = (a[1] + b[1]) / 2
|
||||
const control: LngLat = [midLng - bulgeWest, midLat]
|
||||
const startK = i === 0 ? 0 : 1
|
||||
for (let k = startK; k <= samplesPerSegment; k++) {
|
||||
out.push(quadraticBezier(a, control, b, k / samplesPerSegment))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* 沿路径线性插值增加点(直线段)
|
||||
*/
|
||||
export function interpolatePath(path: LngLat[], stepsPerSegment: number = 4): LngLat[] {
|
||||
if (path.length < 2) return path
|
||||
const out: LngLat[] = []
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const a = path[i]
|
||||
const b = path[i + 1]
|
||||
const startK = i === 0 ? 0 : 1
|
||||
for (let k = startK; k <= stepsPerSegment; k++) {
|
||||
const t = k / stepsPerSegment
|
||||
out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* 锯齿防御线:沿 path 每 toothWidth(比例)生成齿,齿高 toothHeight,朝向法向(敌方侧)
|
||||
* @param path 路径点 [[lng, lat], ...](可先经 interpolatePath 插值)
|
||||
* @param toothWidth 齿宽(与段长同单位,经纬度)
|
||||
* @param toothHeight 齿高(法向伸出)
|
||||
*/
|
||||
export function createDefenseLine(
|
||||
path: number[][],
|
||||
toothWidth: number,
|
||||
toothHeight: number
|
||||
): number[][] {
|
||||
const coords: number[][] = []
|
||||
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const start = path[i] as [number, number]
|
||||
const end = path[i + 1] as [number, number]
|
||||
|
||||
const dx = end[0] - start[0]
|
||||
const dy = end[1] - start[1]
|
||||
const L = Math.sqrt(dx * dx + dy * dy)
|
||||
if (L < 1e-9) continue
|
||||
|
||||
const ux = dx / L
|
||||
const uy = dy / L
|
||||
const nx = -uy
|
||||
const ny = ux
|
||||
|
||||
const numTeeth = Math.max(1, Math.floor(L / toothWidth))
|
||||
const actualWidth = L / numTeeth
|
||||
|
||||
for (let j = 0; j < numTeeth; j++) {
|
||||
const t1 = j * actualWidth
|
||||
const t2 = t1 + actualWidth * 0.5
|
||||
const t3 = (j + 1) * actualWidth
|
||||
|
||||
coords.push([start[0] + ux * t1, start[1] + uy * t1])
|
||||
coords.push([
|
||||
start[0] + ux * t2 + nx * toothHeight,
|
||||
start[1] + uy * t2 + ny * toothHeight,
|
||||
])
|
||||
coords.push([start[0] + ux * t3, start[1] + uy * t3])
|
||||
}
|
||||
}
|
||||
coords.push(path[path.length - 1] as [number, number])
|
||||
return coords
|
||||
}
|
||||
Reference in New Issue
Block a user