From 2a4c9753817b33d60bbd459660b6845ca510e3b0 Mon Sep 17 00:00:00 2001 From: 0x-jerry Date: Tue, 11 Jun 2024 16:57:38 +0800 Subject: [PATCH] feat: a star --- src/canvas/aStar.ts | 77 +++++++++++++++++++++++++++++------------ src/lib/a-star/index.ts | 3 -- src/pages/a-star.vue | 29 ++++++++++++---- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/src/canvas/aStar.ts b/src/canvas/aStar.ts index 669fc8b..844f29e 100644 --- a/src/canvas/aStar.ts +++ b/src/canvas/aStar.ts @@ -1,46 +1,79 @@ import { AStar, CellType } from '@/lib/a-star' export interface DrawAStarOption { - color: string - obstacleColor: string - - /** - * @default 1 - */ - cellSize?: number + cellSize: number } export function drawAStar(ctx: CanvasRenderingContext2D, opt: DrawAStarOption) { - const { cellSize = 1 } = opt + const { cellSize } = opt + const { width, height } = ctx.canvas + const w = ~~(width / cellSize) + const h = ~~(height / cellSize) + + const pathFinding = new AStar(w, h) + + const startPoint = { x: 1, y: 1 } + const endPoint = { x: w - 1, y: h - 1 } - const pathFinding = new AStar(width, height) + return start() - const g = pathFinding.resolve( - { x: 10, y: 10 }, - { x: width - 10, y: height - 10 }, - { + function* start() { + const iterator = pathFinding.resolve(startPoint, endPoint, { heuristic(from, to) { - return Math.sqrt(Math.pow(from.x - to.x, 2) + Math.pow(from.y - to.y, 2)) + return Math.pow(from.x - to.x, 2) + Math.pow(from.y - to.y, 2) }, distance(from, to) { return Math.sqrt(Math.pow(from.x - to.x, 2) + Math.pow(from.y - to.y, 2)) }, - draw, - }, - ) + }) - return g + let v = iterator.next() + + yield + while (!v.done) { + v = iterator.next() + draw() + yield + } + } function draw() { + const colors = { + color: '#ffffff', + visitedColor: '#3c85d2', + obstacleColor: '#f26f6f', + startColor: '#ff0000', + endColor: '#ff0000', + pathColor: '#00ff00', + } + pathFinding.grid.forEach((v, x, y) => { - if ((v ?? 0) & CellType.Walkable) { - ctx.fillStyle = opt.color + if (pathFinding._rearch(startPoint, { x, y })) { + ctx.fillStyle = colors.startColor + } else if (pathFinding._rearch(endPoint, { x, y })) { + ctx.fillStyle = colors.endColor + } else if (pathFinding.cameFrom.has({ x, y })) { + ctx.fillStyle = colors.visitedColor + } else if ((v ?? 0) & CellType.Walkable) { + ctx.fillStyle = colors.color } else { - ctx.fillStyle = opt.obstacleColor + ctx.fillStyle = colors.obstacleColor } - ctx.fillRect(x, y, cellSize, cellSize) + ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize) }) + + const current = pathFinding.openSet.peek() + if (current) { + const output = pathFinding._reconstructPath(current) + + for (const p of output) { + const { x, y } = p + ctx.fillStyle = colors.pathColor + + ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize) + } + } } } diff --git a/src/lib/a-star/index.ts b/src/lib/a-star/index.ts index 220f188..87979c1 100644 --- a/src/lib/a-star/index.ts +++ b/src/lib/a-star/index.ts @@ -3,7 +3,6 @@ import { MinHeap } from 'data-structure-typed' export enum CellType { Walkable = 1 << 1, - Visited = 1 << 3, } class GridMap { @@ -88,7 +87,6 @@ export class AStar { opt: { heuristic: (from: IVec2, to: IVec2) => number distance: (from: IVec2, to: IVec2) => number - draw: () => void }, ) { this.fScore.clear() @@ -110,7 +108,6 @@ export class AStar { this.openSet.add(start) while (!this.openSet.isEmpty()) { - opt.draw() yield const current = this.openSet.poll()! diff --git a/src/pages/a-star.vue b/src/pages/a-star.vue index 57af42a..174986d 100644 --- a/src/pages/a-star.vue +++ b/src/pages/a-star.vue @@ -6,21 +6,36 @@ import { drawAStar } from '@/canvas/aStar' // ______________ const option = useOptionGUI({ - color: '#ffffff', - obstacleColor: '#f26f6f', + cellSize: { + _: 'number', + value: 10, + step: 1, + min: 10, + max: 50, + }, + FPS: { + _: 'number', + step: 1, + value: 60, + min: 1, + max: 120, + }, }) // ----------- -const runner = useCanvasRunner((ctx) => { - const _conf = option.value +const runner = useCanvasRunner( + (ctx) => { + const _conf = option.value - return drawAStar(ctx, _conf) -}) + return drawAStar(ctx, _conf) + }, + { fps: () => option.value.FPS }, +)