@esengine/spatial 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -406,6 +406,9 @@ var _GridAOI = class _GridAOI {
406
406
  this._observers.set(entity, data);
407
407
  this._addToCell(cellKey, data);
408
408
  this._updateVisibility(data);
409
+ if (data.observable) {
410
+ this._updateObserversOfEntity(data);
411
+ }
409
412
  }
410
413
  /**
411
414
  * @zh 移除观察者
@@ -653,36 +656,27 @@ var _GridAOI = class _GridAOI {
653
656
  * @en Update other observers' visibility of an entity
654
657
  */
655
658
  _updateObserversOfEntity(movedData) {
656
- const cellRadius = Math.ceil(this._getMaxViewRange() / this._cellSize) + 1;
657
- const centerCell = this._getCellCoords(movedData.position);
658
- for (let dx = -cellRadius; dx <= cellRadius; dx++) {
659
- for (let dy = -cellRadius; dy <= cellRadius; dy++) {
660
- const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;
661
- const cell = this._cells.get(cellKey);
662
- if (!cell) continue;
663
- for (const otherData of cell) {
664
- if (otherData === movedData) continue;
665
- const distSq = distanceSquared(otherData.position, movedData.position);
666
- const wasVisible = otherData.visibleEntities.has(movedData.entity);
667
- const isVisible = distSq <= otherData.viewRangeSq;
668
- if (isVisible && !wasVisible) {
669
- otherData.visibleEntities.add(movedData.entity);
670
- this._emitEvent({
671
- type: "enter",
672
- observer: otherData.entity,
673
- target: movedData.entity,
674
- position: movedData.position
675
- }, otherData);
676
- } else if (!isVisible && wasVisible) {
677
- otherData.visibleEntities.delete(movedData.entity);
678
- this._emitEvent({
679
- type: "exit",
680
- observer: otherData.entity,
681
- target: movedData.entity,
682
- position: movedData.position
683
- }, otherData);
684
- }
685
- }
659
+ for (const [, otherData] of this._observers) {
660
+ if (otherData === movedData) continue;
661
+ const distSq = distanceSquared(otherData.position, movedData.position);
662
+ const wasVisible = otherData.visibleEntities.has(movedData.entity);
663
+ const isVisible = distSq <= otherData.viewRangeSq;
664
+ if (isVisible && !wasVisible) {
665
+ otherData.visibleEntities.add(movedData.entity);
666
+ this._emitEvent({
667
+ type: "enter",
668
+ observer: otherData.entity,
669
+ target: movedData.entity,
670
+ position: movedData.position
671
+ }, otherData);
672
+ } else if (!isVisible && wasVisible) {
673
+ otherData.visibleEntities.delete(movedData.entity);
674
+ this._emitEvent({
675
+ type: "exit",
676
+ observer: otherData.entity,
677
+ target: movedData.entity,
678
+ position: movedData.position
679
+ }, otherData);
686
680
  }
687
681
  }
688
682
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ISpatialQuery.ts","../src/GridSpatialIndex.ts","../src/aoi/GridAOI.ts","../src/tokens.ts","../src/nodes/SpatialQueryNodes.ts","../src/aoi/AOINodes.ts"],"sourcesContent":["/**\n * @zh 空间查询接口\n * @en Spatial Query Interface\n *\n * @zh 提供空间查询的核心抽象\n * @en Provides core abstractions for spatial queries\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\n\n// =============================================================================\n// 基础类型 | Basic Types\n// =============================================================================\n\n/**\n * @zh 空间边界框\n * @en Spatial bounding box\n */\nexport interface IBounds {\n /**\n * @zh 最小 X 坐标\n * @en Minimum X coordinate\n */\n readonly minX: number;\n\n /**\n * @zh 最小 Y 坐标\n * @en Minimum Y coordinate\n */\n readonly minY: number;\n\n /**\n * @zh 最大 X 坐标\n * @en Maximum X coordinate\n */\n readonly maxX: number;\n\n /**\n * @zh 最大 Y 坐标\n * @en Maximum Y coordinate\n */\n readonly maxY: number;\n}\n\n/**\n * @zh 可定位对象接口\n * @en Positionable object interface\n */\nexport interface IPositionable {\n /**\n * @zh 获取对象位置\n * @en Get object position\n */\n readonly position: IVector2;\n}\n\n/**\n * @zh 可定位且有边界的对象接口\n * @en Positionable object with bounds interface\n */\nexport interface IBoundable extends IPositionable {\n /**\n * @zh 获取对象边界\n * @en Get object bounds\n */\n readonly bounds: IBounds;\n}\n\n/**\n * @zh 射线检测结果\n * @en Raycast hit result\n */\nexport interface IRaycastHit<T> {\n /**\n * @zh 命中的对象\n * @en Hit object\n */\n readonly target: T;\n\n /**\n * @zh 命中点\n * @en Hit point\n */\n readonly point: IVector2;\n\n /**\n * @zh 命中点的法线\n * @en Normal at hit point\n */\n readonly normal: IVector2;\n\n /**\n * @zh 距离射线起点的距离\n * @en Distance from ray origin\n */\n readonly distance: number;\n}\n\n/**\n * @zh 过滤器函数类型\n * @en Filter function type\n */\nexport type SpatialFilter<T> = (item: T) => boolean;\n\n// =============================================================================\n// 空间查询接口 | Spatial Query Interface\n// =============================================================================\n\n/**\n * @zh 空间查询接口\n * @en Spatial query interface\n *\n * @zh 提供空间查询能力,支持范围查询、最近邻查询和射线检测\n * @en Provides spatial query capabilities including range queries, nearest neighbor queries, and raycasting\n */\nexport interface ISpatialQuery<T> {\n /**\n * @zh 查找半径内的所有对象\n * @en Find all objects within radius\n *\n * @param center - @zh 中心点 @en Center point\n * @param radius - @zh 半径 @en Radius\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 半径内的对象数组 @en Array of objects within radius\n */\n findInRadius(center: IVector2, radius: number, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 查找矩形区域内的所有对象\n * @en Find all objects within rectangle\n *\n * @param bounds - @zh 边界框 @en Bounding box\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 区域内的对象数组 @en Array of objects within bounds\n */\n findInRect(bounds: IBounds, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 查找最近的对象\n * @en Find nearest object\n *\n * @param center - @zh 查询中心点 @en Query center point\n * @param maxDistance - @zh 最大搜索距离 @en Maximum search distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 最近的对象,没有则返回 null @en Nearest object or null\n */\n findNearest(center: IVector2, maxDistance?: number, filter?: SpatialFilter<T>): T | null;\n\n /**\n * @zh 查找最近的 K 个对象\n * @en Find K nearest objects\n *\n * @param center - @zh 查询中心点 @en Query center point\n * @param k - @zh 返回的对象数量 @en Number of objects to return\n * @param maxDistance - @zh 最大搜索距离 @en Maximum search distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 最近的 K 个对象数组 @en Array of K nearest objects\n */\n findKNearest(center: IVector2, k: number, maxDistance?: number, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 射线检测\n * @en Raycast\n *\n * @param origin - @zh 射线起点 @en Ray origin\n * @param direction - @zh 射线方向(应归一化)@en Ray direction (should be normalized)\n * @param maxDistance - @zh 最大检测距离 @en Maximum detection distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 命中结果数组,按距离排序 @en Array of hit results sorted by distance\n */\n raycast(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T>[];\n\n /**\n * @zh 射线检测(仅返回第一个命中)\n * @en Raycast (return first hit only)\n *\n * @param origin - @zh 射线起点 @en Ray origin\n * @param direction - @zh 射线方向(应归一化)@en Ray direction (should be normalized)\n * @param maxDistance - @zh 最大检测距离 @en Maximum detection distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 第一个命中结果,没有则返回 null @en First hit result or null\n */\n raycastFirst(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T> | null;\n}\n\n// =============================================================================\n// 空间索引接口 | Spatial Index Interface\n// =============================================================================\n\n/**\n * @zh 空间索引接口\n * @en Spatial index interface\n *\n * @zh 提供空间索引的管理能力\n * @en Provides spatial index management capabilities\n */\nexport interface ISpatialIndex<T> extends ISpatialQuery<T> {\n /**\n * @zh 插入对象\n * @en Insert object\n *\n * @param item - @zh 要插入的对象 @en Object to insert\n * @param position - @zh 对象位置 @en Object position\n */\n insert(item: T, position: IVector2): void;\n\n /**\n * @zh 移除对象\n * @en Remove object\n *\n * @param item - @zh 要移除的对象 @en Object to remove\n * @returns @zh 是否成功移除 @en Whether removal was successful\n */\n remove(item: T): boolean;\n\n /**\n * @zh 更新对象位置\n * @en Update object position\n *\n * @param item - @zh 要更新的对象 @en Object to update\n * @param newPosition - @zh 新位置 @en New position\n * @returns @zh 是否成功更新 @en Whether update was successful\n */\n update(item: T, newPosition: IVector2): boolean;\n\n /**\n * @zh 清空索引\n * @en Clear index\n */\n clear(): void;\n\n /**\n * @zh 获取索引中的对象数量\n * @en Get number of objects in index\n */\n readonly count: number;\n\n /**\n * @zh 获取所有对象\n * @en Get all objects\n */\n getAll(): T[];\n}\n\n// =============================================================================\n// 工具函数 | Utility Functions\n// =============================================================================\n\n/**\n * @zh 创建边界框\n * @en Create bounding box\n */\nexport function createBounds(minX: number, minY: number, maxX: number, maxY: number): IBounds {\n return { minX, minY, maxX, maxY };\n}\n\n/**\n * @zh 从中心点和尺寸创建边界框\n * @en Create bounding box from center and size\n */\nexport function createBoundsFromCenter(center: IVector2, width: number, height: number): IBounds {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n return {\n minX: center.x - halfWidth,\n minY: center.y - halfHeight,\n maxX: center.x + halfWidth,\n maxY: center.y + halfHeight\n };\n}\n\n/**\n * @zh 从圆形创建边界框\n * @en Create bounding box from circle\n */\nexport function createBoundsFromCircle(center: IVector2, radius: number): IBounds {\n return {\n minX: center.x - radius,\n minY: center.y - radius,\n maxX: center.x + radius,\n maxY: center.y + radius\n };\n}\n\n/**\n * @zh 检查点是否在边界框内\n * @en Check if point is inside bounds\n */\nexport function isPointInBounds(point: IVector2, bounds: IBounds): boolean {\n return point.x >= bounds.minX && point.x <= bounds.maxX &&\n point.y >= bounds.minY && point.y <= bounds.maxY;\n}\n\n/**\n * @zh 检查两个边界框是否相交\n * @en Check if two bounding boxes intersect\n */\nexport function boundsIntersect(a: IBounds, b: IBounds): boolean {\n return a.minX <= b.maxX && a.maxX >= b.minX &&\n a.minY <= b.maxY && a.maxY >= b.minY;\n}\n\n/**\n * @zh 检查边界框是否与圆形相交\n * @en Check if bounds intersects with circle\n */\nexport function boundsIntersectsCircle(bounds: IBounds, center: IVector2, radius: number): boolean {\n const closestX = Math.max(bounds.minX, Math.min(center.x, bounds.maxX));\n const closestY = Math.max(bounds.minY, Math.min(center.y, bounds.maxY));\n const distanceX = center.x - closestX;\n const distanceY = center.y - closestY;\n return (distanceX * distanceX + distanceY * distanceY) <= (radius * radius);\n}\n\n/**\n * @zh 计算两点之间的距离平方\n * @en Calculate squared distance between two points\n */\nexport function distanceSquared(a: IVector2, b: IVector2): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * @zh 计算两点之间的距离\n * @en Calculate distance between two points\n */\nexport function distance(a: IVector2, b: IVector2): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n","/**\n * @zh 网格空间索引\n * @en Grid Spatial Index\n *\n * @zh 基于均匀网格的空间索引实现\n * @en Uniform grid based spatial index implementation\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\nimport type {\n ISpatialIndex,\n IBounds,\n IRaycastHit,\n SpatialFilter\n} from './ISpatialQuery';\nimport {\n createBoundsFromCircle,\n boundsIntersectsCircle,\n distanceSquared,\n distance\n} from './ISpatialQuery';\n\n// =============================================================================\n// 网格项 | Grid Item\n// =============================================================================\n\n/**\n * @zh 网格中的项\n * @en Item in grid\n */\ninterface GridItem<T> {\n item: T;\n position: IVector2;\n cellKey: string;\n}\n\n// =============================================================================\n// 网格空间索引 | Grid Spatial Index\n// =============================================================================\n\n/**\n * @zh 网格空间索引配置\n * @en Grid spatial index configuration\n */\nexport interface GridSpatialIndexConfig {\n /**\n * @zh 网格单元格大小\n * @en Grid cell size\n */\n cellSize: number;\n}\n\n/**\n * @zh 网格空间索引实现\n * @en Grid spatial index implementation\n *\n * @zh 使用均匀网格进行空间划分,适合对象分布均匀的场景\n * @en Uses uniform grid for spatial partitioning, suitable for evenly distributed objects\n */\nexport class GridSpatialIndex<T> implements ISpatialIndex<T> {\n private readonly _cellSize: number;\n private readonly _cells: Map<string, Set<GridItem<T>>> = new Map();\n private readonly _itemMap: Map<T, GridItem<T>> = new Map();\n\n constructor(config: GridSpatialIndexConfig) {\n this._cellSize = config.cellSize;\n }\n\n // =========================================================================\n // ISpatialIndex 实现 | ISpatialIndex Implementation\n // =========================================================================\n\n get count(): number {\n return this._itemMap.size;\n }\n\n /**\n * @zh 插入对象\n * @en Insert object\n */\n insert(item: T, position: IVector2): void {\n if (this._itemMap.has(item)) {\n this.update(item, position);\n return;\n }\n\n const cellKey = this._getCellKey(position);\n const gridItem: GridItem<T> = { item, position: { x: position.x, y: position.y }, cellKey };\n\n this._itemMap.set(item, gridItem);\n this._addToCell(cellKey, gridItem);\n }\n\n /**\n * @zh 移除对象\n * @en Remove object\n */\n remove(item: T): boolean {\n const gridItem = this._itemMap.get(item);\n if (!gridItem) {\n return false;\n }\n\n this._removeFromCell(gridItem.cellKey, gridItem);\n this._itemMap.delete(item);\n return true;\n }\n\n /**\n * @zh 更新对象位置\n * @en Update object position\n */\n update(item: T, newPosition: IVector2): boolean {\n const gridItem = this._itemMap.get(item);\n if (!gridItem) {\n return false;\n }\n\n const newCellKey = this._getCellKey(newPosition);\n\n if (newCellKey !== gridItem.cellKey) {\n this._removeFromCell(gridItem.cellKey, gridItem);\n gridItem.cellKey = newCellKey;\n this._addToCell(newCellKey, gridItem);\n }\n\n gridItem.position = { x: newPosition.x, y: newPosition.y };\n return true;\n }\n\n /**\n * @zh 清空索引\n * @en Clear index\n */\n clear(): void {\n this._cells.clear();\n this._itemMap.clear();\n }\n\n /**\n * @zh 获取所有对象\n * @en Get all objects\n */\n getAll(): T[] {\n return Array.from(this._itemMap.keys());\n }\n\n // =========================================================================\n // ISpatialQuery 实现 | ISpatialQuery Implementation\n // =========================================================================\n\n /**\n * @zh 查找半径内的所有对象\n * @en Find all objects within radius\n */\n findInRadius(center: IVector2, radius: number, filter?: SpatialFilter<T>): T[] {\n const results: T[] = [];\n const radiusSq = radius * radius;\n const bounds = createBoundsFromCircle(center, radius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq <= radiusSq) {\n if (!filter || filter(gridItem.item)) {\n results.push(gridItem.item);\n }\n }\n });\n\n return results;\n }\n\n /**\n * @zh 查找矩形区域内的所有对象\n * @en Find all objects within rectangle\n */\n findInRect(bounds: IBounds, filter?: SpatialFilter<T>): T[] {\n const results: T[] = [];\n\n this._forEachInBounds(bounds, (gridItem) => {\n const pos = gridItem.position;\n if (pos.x >= bounds.minX && pos.x <= bounds.maxX &&\n pos.y >= bounds.minY && pos.y <= bounds.maxY) {\n if (!filter || filter(gridItem.item)) {\n results.push(gridItem.item);\n }\n }\n });\n\n return results;\n }\n\n /**\n * @zh 查找最近的对象\n * @en Find nearest object\n */\n findNearest(center: IVector2, maxDistance?: number, filter?: SpatialFilter<T>): T | null {\n let nearest: T | null = null;\n let nearestDistSq = maxDistance !== undefined ? maxDistance * maxDistance : Infinity;\n\n const searchRadius = maxDistance ?? this._cellSize * 10;\n const bounds = createBoundsFromCircle(center, searchRadius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq < nearestDistSq) {\n if (!filter || filter(gridItem.item)) {\n nearest = gridItem.item;\n nearestDistSq = distSq;\n }\n }\n });\n\n return nearest;\n }\n\n /**\n * @zh 查找最近的 K 个对象\n * @en Find K nearest objects\n */\n findKNearest(center: IVector2, k: number, maxDistance?: number, filter?: SpatialFilter<T>): T[] {\n if (k <= 0) return [];\n\n const candidates: Array<{ item: T; distSq: number }> = [];\n const maxDistSq = maxDistance !== undefined ? maxDistance * maxDistance : Infinity;\n const searchRadius = maxDistance ?? this._cellSize * 10;\n const bounds = createBoundsFromCircle(center, searchRadius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq <= maxDistSq) {\n if (!filter || filter(gridItem.item)) {\n candidates.push({ item: gridItem.item, distSq });\n }\n }\n });\n\n candidates.sort((a, b) => a.distSq - b.distSq);\n return candidates.slice(0, k).map(c => c.item);\n }\n\n /**\n * @zh 射线检测\n * @en Raycast\n */\n raycast(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T>[] {\n const hits: IRaycastHit<T>[] = [];\n const rayBounds = this._getRayBounds(origin, direction, maxDistance);\n\n this._forEachInBounds(rayBounds, (gridItem) => {\n const hit = this._rayIntersectsPoint(origin, direction, gridItem.position, maxDistance);\n if (hit && hit.distance <= maxDistance) {\n if (!filter || filter(gridItem.item)) {\n hits.push({\n target: gridItem.item,\n point: hit.point,\n normal: hit.normal,\n distance: hit.distance\n });\n }\n }\n });\n\n hits.sort((a, b) => a.distance - b.distance);\n return hits;\n }\n\n /**\n * @zh 射线检测(仅返回第一个命中)\n * @en Raycast (return first hit only)\n */\n raycastFirst(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T> | null {\n const hits = this.raycast(origin, direction, maxDistance, filter);\n return hits.length > 0 ? hits[0] : null;\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n private _getCellKey(position: IVector2): string {\n const cellX = Math.floor(position.x / this._cellSize);\n const cellY = Math.floor(position.y / this._cellSize);\n return `${cellX},${cellY}`;\n }\n\n private _getCellCoords(position: IVector2): { x: number; y: number } {\n return {\n x: Math.floor(position.x / this._cellSize),\n y: Math.floor(position.y / this._cellSize)\n };\n }\n\n private _addToCell(cellKey: string, gridItem: GridItem<T>): void {\n let cell = this._cells.get(cellKey);\n if (!cell) {\n cell = new Set();\n this._cells.set(cellKey, cell);\n }\n cell.add(gridItem);\n }\n\n private _removeFromCell(cellKey: string, gridItem: GridItem<T>): void {\n const cell = this._cells.get(cellKey);\n if (cell) {\n cell.delete(gridItem);\n if (cell.size === 0) {\n this._cells.delete(cellKey);\n }\n }\n }\n\n private _forEachInBounds(bounds: IBounds, callback: (item: GridItem<T>) => void): void {\n const minCell = this._getCellCoords({ x: bounds.minX, y: bounds.minY });\n const maxCell = this._getCellCoords({ x: bounds.maxX, y: bounds.maxY });\n\n for (let x = minCell.x; x <= maxCell.x; x++) {\n for (let y = minCell.y; y <= maxCell.y; y++) {\n const cell = this._cells.get(`${x},${y}`);\n if (cell) {\n for (const gridItem of cell) {\n callback(gridItem);\n }\n }\n }\n }\n }\n\n private _getRayBounds(origin: IVector2, direction: IVector2, maxDistance: number): IBounds {\n const endX = origin.x + direction.x * maxDistance;\n const endY = origin.y + direction.y * maxDistance;\n\n return {\n minX: Math.min(origin.x, endX),\n minY: Math.min(origin.y, endY),\n maxX: Math.max(origin.x, endX),\n maxY: Math.max(origin.y, endY)\n };\n }\n\n private _rayIntersectsPoint(\n origin: IVector2,\n direction: IVector2,\n point: IVector2,\n _maxDistance: number,\n hitRadius: number = 0.5\n ): { point: IVector2; normal: IVector2; distance: number } | null {\n const toPoint = { x: point.x - origin.x, y: point.y - origin.y };\n const projection = toPoint.x * direction.x + toPoint.y * direction.y;\n\n if (projection < 0) {\n return null;\n }\n\n const closestX = origin.x + direction.x * projection;\n const closestY = origin.y + direction.y * projection;\n const distToLine = Math.sqrt(\n (point.x - closestX) * (point.x - closestX) +\n (point.y - closestY) * (point.y - closestY)\n );\n\n if (distToLine <= hitRadius) {\n const hitDist = projection - Math.sqrt(hitRadius * hitRadius - distToLine * distToLine);\n if (hitDist >= 0) {\n const hitPoint = {\n x: origin.x + direction.x * hitDist,\n y: origin.y + direction.y * hitDist\n };\n const normal = {\n x: hitPoint.x - point.x,\n y: hitPoint.y - point.y\n };\n const normalLen = Math.sqrt(normal.x * normal.x + normal.y * normal.y);\n if (normalLen > 0) {\n normal.x /= normalLen;\n normal.y /= normalLen;\n }\n return { point: hitPoint, normal, distance: hitDist };\n }\n }\n\n return null;\n }\n}\n\n// =============================================================================\n// 工厂函数 | Factory Functions\n// =============================================================================\n\n/**\n * @zh 创建网格空间索引\n * @en Create grid spatial index\n */\nexport function createGridSpatialIndex<T>(cellSize: number = 100): GridSpatialIndex<T> {\n return new GridSpatialIndex<T>({ cellSize });\n}\n","/**\n * @zh 网格 AOI 实现\n * @en Grid AOI Implementation\n *\n * @zh 基于均匀网格的兴趣区域管理实现\n * @en Uniform grid based area of interest management implementation\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\nimport type {\n IAOIManager,\n IAOIObserverConfig,\n IAOIEvent,\n AOIEventListener\n} from './IAOI';\nimport { distanceSquared } from '../ISpatialQuery';\n\n// =============================================================================\n// 内部类型 | Internal Types\n// =============================================================================\n\n/**\n * @zh AOI 观察者数据\n * @en AOI observer data\n */\ninterface AOIObserverData<T> {\n entity: T;\n position: IVector2;\n viewRange: number;\n viewRangeSq: number;\n observable: boolean;\n cellKey: string;\n /** @zh 当前可见的实体集合 @en Currently visible entities */\n visibleEntities: Set<T>;\n /** @zh 实体特定的监听器 @en Entity-specific listeners */\n listeners: Set<AOIEventListener<T>>;\n}\n\n// =============================================================================\n// 网格 AOI 配置 | Grid AOI Configuration\n// =============================================================================\n\n/**\n * @zh 网格 AOI 配置\n * @en Grid AOI configuration\n */\nexport interface GridAOIConfig {\n /**\n * @zh 网格单元格大小(建议设置为平均视野范围的 1-2 倍)\n * @en Grid cell size (recommended 1-2x average view range)\n */\n cellSize: number;\n}\n\n// =============================================================================\n// 网格 AOI 实现 | Grid AOI Implementation\n// =============================================================================\n\n/**\n * @zh 网格 AOI 实现\n * @en Grid AOI implementation\n *\n * @zh 使用均匀网格进行空间划分,高效管理大量实体的兴趣区域\n * @en Uses uniform grid for spatial partitioning, efficiently managing AOI for many entities\n */\nexport class GridAOI<T> implements IAOIManager<T> {\n private readonly _cellSize: number;\n private readonly _cells: Map<string, Set<AOIObserverData<T>>> = new Map();\n private readonly _observers: Map<T, AOIObserverData<T>> = new Map();\n private readonly _globalListeners: Set<AOIEventListener<T>> = new Set();\n\n constructor(config: GridAOIConfig) {\n this._cellSize = config.cellSize;\n }\n\n // =========================================================================\n // IAOIManager 实现 | IAOIManager Implementation\n // =========================================================================\n\n get count(): number {\n return this._observers.size;\n }\n\n /**\n * @zh 添加观察者\n * @en Add observer\n */\n addObserver(entity: T, position: IVector2, config: IAOIObserverConfig): void {\n if (this._observers.has(entity)) {\n this.updatePosition(entity, position);\n this.updateViewRange(entity, config.viewRange);\n return;\n }\n\n const cellKey = this._getCellKey(position);\n const data: AOIObserverData<T> = {\n entity,\n position: { x: position.x, y: position.y },\n viewRange: config.viewRange,\n viewRangeSq: config.viewRange * config.viewRange,\n observable: config.observable !== false,\n cellKey,\n visibleEntities: new Set(),\n listeners: new Set()\n };\n\n this._observers.set(entity, data);\n this._addToCell(cellKey, data);\n\n // Initial visibility check\n this._updateVisibility(data);\n }\n\n /**\n * @zh 移除观察者\n * @en Remove observer\n */\n removeObserver(entity: T): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n // Notify all observers who were watching this entity\n if (data.observable) {\n for (const [, otherData] of this._observers) {\n if (otherData !== data && otherData.visibleEntities.has(entity)) {\n otherData.visibleEntities.delete(entity);\n this._emitEvent({\n type: 'exit',\n observer: otherData.entity,\n target: entity,\n position: data.position\n }, otherData);\n }\n }\n }\n\n // Notify this entity about all entities it was watching\n for (const visible of data.visibleEntities) {\n const visibleData = this._observers.get(visible);\n if (visibleData) {\n this._emitEvent({\n type: 'exit',\n observer: entity,\n target: visible,\n position: visibleData.position\n }, data);\n }\n }\n\n this._removeFromCell(data.cellKey, data);\n this._observers.delete(entity);\n return true;\n }\n\n /**\n * @zh 更新观察者位置\n * @en Update observer position\n */\n updatePosition(entity: T, newPosition: IVector2): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n const newCellKey = this._getCellKey(newPosition);\n\n // Update cell if changed\n if (newCellKey !== data.cellKey) {\n this._removeFromCell(data.cellKey, data);\n data.cellKey = newCellKey;\n this._addToCell(newCellKey, data);\n }\n\n data.position = { x: newPosition.x, y: newPosition.y };\n\n // Update visibility for this observer\n this._updateVisibility(data);\n\n // Update visibility for others who might now see/unsee this entity\n if (data.observable) {\n this._updateObserversOfEntity(data);\n }\n\n return true;\n }\n\n /**\n * @zh 更新观察者视野范围\n * @en Update observer view range\n */\n updateViewRange(entity: T, newRange: number): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n data.viewRange = newRange;\n data.viewRangeSq = newRange * newRange;\n\n // Recalculate visibility\n this._updateVisibility(data);\n\n return true;\n }\n\n /**\n * @zh 获取实体视野内的所有对象\n * @en Get all objects within entity's view\n */\n getEntitiesInView(entity: T): T[] {\n const data = this._observers.get(entity);\n if (!data) {\n return [];\n }\n return Array.from(data.visibleEntities);\n }\n\n /**\n * @zh 获取能看到指定实体的所有观察者\n * @en Get all observers who can see the specified entity\n */\n getObserversOf(entity: T): T[] {\n const data = this._observers.get(entity);\n if (!data || !data.observable) {\n return [];\n }\n\n const observers: T[] = [];\n for (const [, otherData] of this._observers) {\n if (otherData !== data && otherData.visibleEntities.has(entity)) {\n observers.push(otherData.entity);\n }\n }\n return observers;\n }\n\n /**\n * @zh 检查观察者是否能看到目标\n * @en Check if observer can see target\n */\n canSee(observer: T, target: T): boolean {\n const data = this._observers.get(observer);\n if (!data) {\n return false;\n }\n return data.visibleEntities.has(target);\n }\n\n /**\n * @zh 添加全局事件监听器\n * @en Add global event listener\n */\n addListener(listener: AOIEventListener<T>): void {\n this._globalListeners.add(listener);\n }\n\n /**\n * @zh 移除全局事件监听器\n * @en Remove global event listener\n */\n removeListener(listener: AOIEventListener<T>): void {\n this._globalListeners.delete(listener);\n }\n\n /**\n * @zh 为特定观察者添加事件监听器\n * @en Add event listener for specific observer\n */\n addEntityListener(entity: T, listener: AOIEventListener<T>): void {\n const data = this._observers.get(entity);\n if (data) {\n data.listeners.add(listener);\n }\n }\n\n /**\n * @zh 移除特定观察者的事件监听器\n * @en Remove event listener for specific observer\n */\n removeEntityListener(entity: T, listener: AOIEventListener<T>): void {\n const data = this._observers.get(entity);\n if (data) {\n data.listeners.delete(listener);\n }\n }\n\n /**\n * @zh 清空所有观察者\n * @en Clear all observers\n */\n clear(): void {\n this._cells.clear();\n this._observers.clear();\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n private _getCellKey(position: IVector2): string {\n const cellX = Math.floor(position.x / this._cellSize);\n const cellY = Math.floor(position.y / this._cellSize);\n return `${cellX},${cellY}`;\n }\n\n private _getCellCoords(position: IVector2): { x: number; y: number } {\n return {\n x: Math.floor(position.x / this._cellSize),\n y: Math.floor(position.y / this._cellSize)\n };\n }\n\n private _addToCell(cellKey: string, data: AOIObserverData<T>): void {\n let cell = this._cells.get(cellKey);\n if (!cell) {\n cell = new Set();\n this._cells.set(cellKey, cell);\n }\n cell.add(data);\n }\n\n private _removeFromCell(cellKey: string, data: AOIObserverData<T>): void {\n const cell = this._cells.get(cellKey);\n if (cell) {\n cell.delete(data);\n if (cell.size === 0) {\n this._cells.delete(cellKey);\n }\n }\n }\n\n /**\n * @zh 更新观察者的可见实体列表\n * @en Update observer's visible entities list\n */\n private _updateVisibility(data: AOIObserverData<T>): void {\n const newVisible = new Set<T>();\n\n // Calculate search radius in cells\n const cellRadius = Math.ceil(data.viewRange / this._cellSize);\n const centerCell = this._getCellCoords(data.position);\n\n // Check all cells within range\n for (let dx = -cellRadius; dx <= cellRadius; dx++) {\n for (let dy = -cellRadius; dy <= cellRadius; dy++) {\n const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;\n const cell = this._cells.get(cellKey);\n if (!cell) continue;\n\n for (const otherData of cell) {\n if (otherData === data) continue;\n if (!otherData.observable) continue;\n\n const distSq = distanceSquared(data.position, otherData.position);\n if (distSq <= data.viewRangeSq) {\n newVisible.add(otherData.entity);\n }\n }\n }\n }\n\n // Find entities that entered view\n for (const entity of newVisible) {\n if (!data.visibleEntities.has(entity)) {\n const targetData = this._observers.get(entity);\n if (targetData) {\n this._emitEvent({\n type: 'enter',\n observer: data.entity,\n target: entity,\n position: targetData.position\n }, data);\n }\n }\n }\n\n // Find entities that exited view\n for (const entity of data.visibleEntities) {\n if (!newVisible.has(entity)) {\n const targetData = this._observers.get(entity);\n const position = targetData?.position ?? { x: 0, y: 0 };\n this._emitEvent({\n type: 'exit',\n observer: data.entity,\n target: entity,\n position\n }, data);\n }\n }\n\n data.visibleEntities = newVisible;\n }\n\n /**\n * @zh 更新其他观察者对于某个实体的可见性\n * @en Update other observers' visibility of an entity\n */\n private _updateObserversOfEntity(movedData: AOIObserverData<T>): void {\n const cellRadius = Math.ceil(this._getMaxViewRange() / this._cellSize) + 1;\n const centerCell = this._getCellCoords(movedData.position);\n\n for (let dx = -cellRadius; dx <= cellRadius; dx++) {\n for (let dy = -cellRadius; dy <= cellRadius; dy++) {\n const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;\n const cell = this._cells.get(cellKey);\n if (!cell) continue;\n\n for (const otherData of cell) {\n if (otherData === movedData) continue;\n\n const distSq = distanceSquared(otherData.position, movedData.position);\n const wasVisible = otherData.visibleEntities.has(movedData.entity);\n const isVisible = distSq <= otherData.viewRangeSq;\n\n if (isVisible && !wasVisible) {\n otherData.visibleEntities.add(movedData.entity);\n this._emitEvent({\n type: 'enter',\n observer: otherData.entity,\n target: movedData.entity,\n position: movedData.position\n }, otherData);\n } else if (!isVisible && wasVisible) {\n otherData.visibleEntities.delete(movedData.entity);\n this._emitEvent({\n type: 'exit',\n observer: otherData.entity,\n target: movedData.entity,\n position: movedData.position\n }, otherData);\n }\n }\n }\n }\n }\n\n /**\n * @zh 获取最大视野范围(用于优化搜索)\n * @en Get maximum view range (for search optimization)\n */\n private _getMaxViewRange(): number {\n let max = 0;\n for (const [, data] of this._observers) {\n if (data.viewRange > max) {\n max = data.viewRange;\n }\n }\n return max;\n }\n\n /**\n * @zh 发送事件\n * @en Emit event\n */\n private _emitEvent(event: IAOIEvent<T>, observerData: AOIObserverData<T>): void {\n // Entity-specific listeners\n for (const listener of observerData.listeners) {\n try {\n listener(event);\n } catch (e) {\n console.error('AOI entity listener error:', e);\n }\n }\n\n // Global listeners\n for (const listener of this._globalListeners) {\n try {\n listener(event);\n } catch (e) {\n console.error('AOI global listener error:', e);\n }\n }\n }\n}\n\n// =============================================================================\n// 工厂函数 | Factory Functions\n// =============================================================================\n\n/**\n * @zh 创建网格 AOI 管理器\n * @en Create grid AOI manager\n *\n * @param cellSize - @zh 网格单元格大小 @en Grid cell size\n */\nexport function createGridAOI<T>(cellSize: number = 100): GridAOI<T> {\n return new GridAOI<T>({ cellSize });\n}\n","/**\n * @zh 空间查询服务令牌\n * @en Spatial Query Service Tokens\n */\n\nimport { createServiceToken } from '@esengine/ecs-framework';\nimport type { ISpatialIndex, ISpatialQuery } from './ISpatialQuery';\nimport type { IAOIManager } from './aoi/IAOI';\n\n/**\n * @zh 空间索引服务令牌\n * @en Spatial index service token\n *\n * @zh 用于注入空间索引服务\n * @en Used for injecting spatial index service\n */\nexport const SpatialIndexToken = createServiceToken<ISpatialIndex<unknown>>('spatialIndex');\n\n/**\n * @zh 空间查询服务令牌\n * @en Spatial query service token\n *\n * @zh 用于注入空间查询服务(只读)\n * @en Used for injecting spatial query service (read-only)\n */\nexport const SpatialQueryToken = createServiceToken<ISpatialQuery<unknown>>('spatialQuery');\n\n/**\n * @zh AOI 管理器服务令牌\n * @en AOI manager service token\n *\n * @zh 用于注入 AOI 兴趣区域管理服务\n * @en Used for injecting AOI (Area of Interest) manager service\n */\nexport const AOIManagerToken = createServiceToken<IAOIManager<unknown>>('aoiManager');\n","/**\n * @zh 空间查询蓝图节点\n * @en Spatial Query Blueprint Nodes\n *\n * @zh 提供空间查询功能的蓝图节点\n * @en Provides blueprint nodes for spatial query functionality\n */\n\nimport type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';\nimport type { ISpatialQuery, IBounds } from '../ISpatialQuery';\n\n// =============================================================================\n// 执行上下文接口 | Execution Context Interface\n// =============================================================================\n\n/**\n * @zh 空间查询上下文\n * @en Spatial query context\n */\ninterface SpatialContext {\n spatialQuery: ISpatialQuery<unknown>;\n evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;\n setOutputs(nodeId: string, outputs: Record<string, unknown>): void;\n}\n\n// =============================================================================\n// FindInRadius 节点 | FindInRadius Node\n// =============================================================================\n\n/**\n * @zh FindInRadius 节点模板\n * @en FindInRadius node template\n */\nexport const FindInRadiusTemplate: BlueprintNodeTemplate = {\n type: 'FindInRadius',\n title: 'Find In Radius',\n category: 'entity',\n description: 'Find all objects within radius / 查找半径内的所有对象',\n keywords: ['spatial', 'find', 'radius', 'range', 'query'],\n menuPath: ['Spatial', 'Find In Radius'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'radius',\n displayName: 'Radius',\n type: 'float',\n defaultValue: 100\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindInRadius 节点执行器\n * @en FindInRadius node executor\n */\nexport class FindInRadiusExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const radius = ctx.evaluateInput(node.id, 'radius', 100) as number;\n\n const results = ctx.spatialQuery?.findInRadius({ x: centerX, y: centerY }, radius) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// FindInRect 节点 | FindInRect Node\n// =============================================================================\n\n/**\n * @zh FindInRect 节点模板\n * @en FindInRect node template\n */\nexport const FindInRectTemplate: BlueprintNodeTemplate = {\n type: 'FindInRect',\n title: 'Find In Rect',\n category: 'entity',\n description: 'Find all objects within rectangle / 查找矩形区域内的所有对象',\n keywords: ['spatial', 'find', 'rect', 'rectangle', 'area', 'query'],\n menuPath: ['Spatial', 'Find In Rect'],\n isPure: true,\n inputs: [\n {\n name: 'minX',\n displayName: 'Min X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'minY',\n displayName: 'Min Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxX',\n displayName: 'Max X',\n type: 'float',\n defaultValue: 100\n },\n {\n name: 'maxY',\n displayName: 'Max Y',\n type: 'float',\n defaultValue: 100\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindInRect 节点执行器\n * @en FindInRect node executor\n */\nexport class FindInRectExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const minX = ctx.evaluateInput(node.id, 'minX', 0) as number;\n const minY = ctx.evaluateInput(node.id, 'minY', 0) as number;\n const maxX = ctx.evaluateInput(node.id, 'maxX', 100) as number;\n const maxY = ctx.evaluateInput(node.id, 'maxY', 100) as number;\n\n const bounds: IBounds = { minX, minY, maxX, maxY };\n const results = ctx.spatialQuery?.findInRect(bounds) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// FindNearest 节点 | FindNearest Node\n// =============================================================================\n\n/**\n * @zh FindNearest 节点模板\n * @en FindNearest node template\n */\nexport const FindNearestTemplate: BlueprintNodeTemplate = {\n type: 'FindNearest',\n title: 'Find Nearest',\n category: 'entity',\n description: 'Find nearest object to a point / 查找距离点最近的对象',\n keywords: ['spatial', 'find', 'nearest', 'closest', 'query'],\n menuPath: ['Spatial', 'Find Nearest'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'result',\n displayName: 'Result',\n type: 'any'\n },\n {\n name: 'found',\n displayName: 'Found',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindNearest 节点执行器\n * @en FindNearest node executor\n */\nexport class FindNearestExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const result = ctx.spatialQuery?.findNearest({ x: centerX, y: centerY }, maxDistance) ?? null;\n\n return {\n outputs: {\n result,\n found: result !== null\n }\n };\n }\n}\n\n// =============================================================================\n// FindKNearest 节点 | FindKNearest Node\n// =============================================================================\n\n/**\n * @zh FindKNearest 节点模板\n * @en FindKNearest node template\n */\nexport const FindKNearestTemplate: BlueprintNodeTemplate = {\n type: 'FindKNearest',\n title: 'Find K Nearest',\n category: 'entity',\n description: 'Find K nearest objects to a point / 查找距离点最近的 K 个对象',\n keywords: ['spatial', 'find', 'nearest', 'k', 'query'],\n menuPath: ['Spatial', 'Find K Nearest'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'k',\n displayName: 'K',\n type: 'int',\n defaultValue: 5\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindKNearest 节点执行器\n * @en FindKNearest node executor\n */\nexport class FindKNearestExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const k = ctx.evaluateInput(node.id, 'k', 5) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const results = ctx.spatialQuery?.findKNearest({ x: centerX, y: centerY }, k, maxDistance) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// Raycast 节点 | Raycast Node\n// =============================================================================\n\n/**\n * @zh Raycast 节点模板\n * @en Raycast node template\n */\nexport const RaycastTemplate: BlueprintNodeTemplate = {\n type: 'Raycast',\n title: 'Raycast',\n category: 'entity',\n description: 'Cast a ray and get all hits / 发射射线并获取所有命中',\n keywords: ['spatial', 'raycast', 'ray', 'hit', 'query'],\n menuPath: ['Spatial', 'Raycast'],\n isPure: true,\n inputs: [\n {\n name: 'originX',\n displayName: 'Origin X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'originY',\n displayName: 'Origin Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'directionX',\n displayName: 'Direction X',\n type: 'float',\n defaultValue: 1\n },\n {\n name: 'directionY',\n displayName: 'Direction Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'hits',\n displayName: 'Hits',\n type: 'array'\n },\n {\n name: 'hitCount',\n displayName: 'Hit Count',\n type: 'int'\n },\n {\n name: 'hasHit',\n displayName: 'Has Hit',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh Raycast 节点执行器\n * @en Raycast node executor\n */\nexport class RaycastExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;\n const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;\n const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;\n const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const hits = ctx.spatialQuery?.raycast(\n { x: originX, y: originY },\n { x: directionX, y: directionY },\n maxDistance\n ) ?? [];\n\n return {\n outputs: {\n hits,\n hitCount: hits.length,\n hasHit: hits.length > 0\n }\n };\n }\n}\n\n/**\n * @zh RaycastFirst 节点模板\n * @en RaycastFirst node template\n */\nexport const RaycastFirstTemplate: BlueprintNodeTemplate = {\n type: 'RaycastFirst',\n title: 'Raycast First',\n category: 'entity',\n description: 'Cast a ray and get first hit / 发射射线并获取第一个命中',\n keywords: ['spatial', 'raycast', 'ray', 'first', 'hit', 'query'],\n menuPath: ['Spatial', 'Raycast First'],\n isPure: true,\n inputs: [\n {\n name: 'originX',\n displayName: 'Origin X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'originY',\n displayName: 'Origin Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'directionX',\n displayName: 'Direction X',\n type: 'float',\n defaultValue: 1\n },\n {\n name: 'directionY',\n displayName: 'Direction Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'hit',\n displayName: 'Hit',\n type: 'object'\n },\n {\n name: 'target',\n displayName: 'Target',\n type: 'any'\n },\n {\n name: 'hitPointX',\n displayName: 'Hit Point X',\n type: 'float'\n },\n {\n name: 'hitPointY',\n displayName: 'Hit Point Y',\n type: 'float'\n },\n {\n name: 'distance',\n displayName: 'Distance',\n type: 'float'\n },\n {\n name: 'hasHit',\n displayName: 'Has Hit',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh RaycastFirst 节点执行器\n * @en RaycastFirst node executor\n */\nexport class RaycastFirstExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;\n const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;\n const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;\n const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const hit = ctx.spatialQuery?.raycastFirst(\n { x: originX, y: originY },\n { x: directionX, y: directionY },\n maxDistance\n ) ?? null;\n\n return {\n outputs: {\n hit,\n target: hit?.target ?? null,\n hitPointX: hit?.point.x ?? 0,\n hitPointY: hit?.point.y ?? 0,\n distance: hit?.distance ?? 0,\n hasHit: hit !== null\n }\n };\n }\n}\n\n// =============================================================================\n// 节点定义集合 | Node Definition Collection\n// =============================================================================\n\n/**\n * @zh 空间查询节点定义\n * @en Spatial query node definitions\n */\nexport const SpatialQueryNodeDefinitions = [\n { template: FindInRadiusTemplate, executor: new FindInRadiusExecutor() },\n { template: FindInRectTemplate, executor: new FindInRectExecutor() },\n { template: FindNearestTemplate, executor: new FindNearestExecutor() },\n { template: FindKNearestTemplate, executor: new FindKNearestExecutor() },\n { template: RaycastTemplate, executor: new RaycastExecutor() },\n { template: RaycastFirstTemplate, executor: new RaycastFirstExecutor() }\n];\n","/**\n * @zh AOI 蓝图节点\n * @en AOI Blueprint Nodes\n *\n * @zh 提供 AOI 功能的蓝图节点\n * @en Provides blueprint nodes for AOI functionality\n */\n\nimport type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';\nimport type { IAOIManager } from './IAOI';\n\n// =============================================================================\n// 执行上下文接口 | Execution Context Interface\n// =============================================================================\n\n/**\n * @zh AOI 上下文\n * @en AOI context\n */\ninterface AOIContext {\n aoiManager: IAOIManager<unknown>;\n entity: unknown;\n evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;\n setOutputs(nodeId: string, outputs: Record<string, unknown>): void;\n}\n\n// =============================================================================\n// GetEntitiesInView 节点 | GetEntitiesInView Node\n// =============================================================================\n\n/**\n * @zh GetEntitiesInView 节点模板\n * @en GetEntitiesInView node template\n */\nexport const GetEntitiesInViewTemplate: BlueprintNodeTemplate = {\n type: 'GetEntitiesInView',\n title: 'Get Entities In View',\n category: 'entity',\n description: 'Get all entities within view range / 获取视野范围内的所有实体',\n keywords: ['aoi', 'view', 'entities', 'visible'],\n menuPath: ['AOI', 'Get Entities In View'],\n isPure: true,\n inputs: [\n {\n name: 'observer',\n displayName: 'Observer',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'entities',\n displayName: 'Entities',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh GetEntitiesInView 节点执行器\n * @en GetEntitiesInView node executor\n */\nexport class GetEntitiesInViewExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const observer = ctx.evaluateInput(node.id, 'observer', ctx.entity);\n\n const entities = ctx.aoiManager?.getEntitiesInView(observer) ?? [];\n\n return {\n outputs: {\n entities,\n count: entities.length\n }\n };\n }\n}\n\n// =============================================================================\n// GetObserversOf 节点 | GetObserversOf Node\n// =============================================================================\n\n/**\n * @zh GetObserversOf 节点模板\n * @en GetObserversOf node template\n */\nexport const GetObserversOfTemplate: BlueprintNodeTemplate = {\n type: 'GetObserversOf',\n title: 'Get Observers Of',\n category: 'entity',\n description: 'Get all observers who can see the entity / 获取能看到该实体的所有观察者',\n keywords: ['aoi', 'observers', 'watchers', 'visible'],\n menuPath: ['AOI', 'Get Observers Of'],\n isPure: true,\n inputs: [\n {\n name: 'target',\n displayName: 'Target',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'observers',\n displayName: 'Observers',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh GetObserversOf 节点执行器\n * @en GetObserversOf node executor\n */\nexport class GetObserversOfExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const target = ctx.evaluateInput(node.id, 'target', ctx.entity);\n\n const observers = ctx.aoiManager?.getObserversOf(target) ?? [];\n\n return {\n outputs: {\n observers,\n count: observers.length\n }\n };\n }\n}\n\n// =============================================================================\n// CanSee 节点 | CanSee Node\n// =============================================================================\n\n/**\n * @zh CanSee 节点模板\n * @en CanSee node template\n */\nexport const CanSeeTemplate: BlueprintNodeTemplate = {\n type: 'CanSee',\n title: 'Can See',\n category: 'entity',\n description: 'Check if observer can see target / 检查观察者是否能看到目标',\n keywords: ['aoi', 'visibility', 'can', 'see', 'check'],\n menuPath: ['AOI', 'Can See'],\n isPure: true,\n inputs: [\n {\n name: 'observer',\n displayName: 'Observer',\n type: 'object'\n },\n {\n name: 'target',\n displayName: 'Target',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'canSee',\n displayName: 'Can See',\n type: 'bool'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh CanSee 节点执行器\n * @en CanSee node executor\n */\nexport class CanSeeExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const observer = ctx.evaluateInput(node.id, 'observer', ctx.entity);\n const target = ctx.evaluateInput(node.id, 'target', null);\n\n const canSee = ctx.aoiManager?.canSee(observer, target) ?? false;\n\n return {\n outputs: {\n canSee\n }\n };\n }\n}\n\n// =============================================================================\n// OnEntityEnterView 事件节点 | OnEntityEnterView Event Node\n// =============================================================================\n\n/**\n * @zh OnEntityEnterView 事件节点模板\n * @en OnEntityEnterView event node template\n */\nexport const OnEntityEnterViewTemplate: BlueprintNodeTemplate = {\n type: 'EventEntityEnterView',\n title: 'On Entity Enter View',\n category: 'event',\n description: 'Triggered when an entity enters view / 当实体进入视野时触发',\n keywords: ['aoi', 'event', 'enter', 'view', 'visible'],\n menuPath: ['AOI', 'Events', 'On Entity Enter View'],\n color: '#e91e63',\n inputs: [],\n outputs: [\n {\n name: 'exec',\n displayName: '',\n type: 'exec'\n },\n {\n name: 'entity',\n displayName: 'Entity',\n type: 'object'\n },\n {\n name: 'positionX',\n displayName: 'Position X',\n type: 'float'\n },\n {\n name: 'positionY',\n displayName: 'Position Y',\n type: 'float'\n }\n ]\n};\n\n/**\n * @zh OnEntityEnterView 事件执行器\n * @en OnEntityEnterView event executor\n */\nexport class OnEntityEnterViewExecutor implements INodeExecutor {\n execute(_node: BlueprintNode, _context: unknown): ExecutionResult {\n // Event nodes don't execute directly, they are triggered by the runtime\n return { nextExec: 'exec' };\n }\n}\n\n// =============================================================================\n// OnEntityExitView 事件节点 | OnEntityExitView Event Node\n// =============================================================================\n\n/**\n * @zh OnEntityExitView 事件节点模板\n * @en OnEntityExitView event node template\n */\nexport const OnEntityExitViewTemplate: BlueprintNodeTemplate = {\n type: 'EventEntityExitView',\n title: 'On Entity Exit View',\n category: 'event',\n description: 'Triggered when an entity exits view / 当实体离开视野时触发',\n keywords: ['aoi', 'event', 'exit', 'view', 'invisible'],\n menuPath: ['AOI', 'Events', 'On Entity Exit View'],\n color: '#e91e63',\n inputs: [],\n outputs: [\n {\n name: 'exec',\n displayName: '',\n type: 'exec'\n },\n {\n name: 'entity',\n displayName: 'Entity',\n type: 'object'\n },\n {\n name: 'positionX',\n displayName: 'Position X',\n type: 'float'\n },\n {\n name: 'positionY',\n displayName: 'Position Y',\n type: 'float'\n }\n ]\n};\n\n/**\n * @zh OnEntityExitView 事件执行器\n * @en OnEntityExitView event executor\n */\nexport class OnEntityExitViewExecutor implements INodeExecutor {\n execute(_node: BlueprintNode, _context: unknown): ExecutionResult {\n // Event nodes don't execute directly, they are triggered by the runtime\n return { nextExec: 'exec' };\n }\n}\n\n// =============================================================================\n// 节点定义集合 | Node Definition Collection\n// =============================================================================\n\n/**\n * @zh AOI 节点定义集合\n * @en AOI node definition collection\n */\nexport const AOINodeDefinitions = {\n templates: [\n GetEntitiesInViewTemplate,\n GetObserversOfTemplate,\n CanSeeTemplate,\n OnEntityEnterViewTemplate,\n OnEntityExitViewTemplate\n ],\n executors: new Map<string, INodeExecutor>([\n ['GetEntitiesInView', new GetEntitiesInViewExecutor()],\n ['GetObserversOf', new GetObserversOfExecutor()],\n ['CanSee', new CanSeeExecutor()],\n ['EventEntityEnterView', new OnEntityEnterViewExecutor()],\n ['EventEntityExitView', new OnEntityExitViewExecutor()]\n ])\n};\n"],"mappings":";;;;;;AA4PO,SAASA,aAAaC,MAAcC,MAAcC,MAAcC,MAAY;AAC/E,SAAO;IAAEH;IAAMC;IAAMC;IAAMC;EAAK;AACpC;AAFgBJ;AAQT,SAASK,uBAAuBC,QAAkBC,OAAeC,QAAc;AAClF,QAAMC,YAAYF,QAAQ;AAC1B,QAAMG,aAAaF,SAAS;AAC5B,SAAO;IACHP,MAAMK,OAAOK,IAAIF;IACjBP,MAAMI,OAAOM,IAAIF;IACjBP,MAAMG,OAAOK,IAAIF;IACjBL,MAAME,OAAOM,IAAIF;EACrB;AACJ;AATgBL;AAeT,SAASQ,uBAAuBP,QAAkBQ,QAAc;AACnE,SAAO;IACHb,MAAMK,OAAOK,IAAIG;IACjBZ,MAAMI,OAAOM,IAAIE;IACjBX,MAAMG,OAAOK,IAAIG;IACjBV,MAAME,OAAOM,IAAIE;EACrB;AACJ;AAPgBD;AAaT,SAASE,gBAAgBC,OAAiBC,QAAe;AAC5D,SAAOD,MAAML,KAAKM,OAAOhB,QAAQe,MAAML,KAAKM,OAAOd,QAC5Ca,MAAMJ,KAAKK,OAAOf,QAAQc,MAAMJ,KAAKK,OAAOb;AACvD;AAHgBW;AAST,SAASG,gBAAgBC,GAAYC,GAAU;AAClD,SAAOD,EAAElB,QAAQmB,EAAEjB,QAAQgB,EAAEhB,QAAQiB,EAAEnB,QAChCkB,EAAEjB,QAAQkB,EAAEhB,QAAQe,EAAEf,QAAQgB,EAAElB;AAC3C;AAHgBgB;AAST,SAASG,uBAAuBJ,QAAiBX,QAAkBQ,QAAc;AACpF,QAAMQ,WAAWC,KAAKC,IAAIP,OAAOhB,MAAMsB,KAAKE,IAAInB,OAAOK,GAAGM,OAAOd,IAAI,CAAA;AACrE,QAAMuB,WAAWH,KAAKC,IAAIP,OAAOf,MAAMqB,KAAKE,IAAInB,OAAOM,GAAGK,OAAOb,IAAI,CAAA;AACrE,QAAMuB,YAAYrB,OAAOK,IAAIW;AAC7B,QAAMM,YAAYtB,OAAOM,IAAIc;AAC7B,SAAQC,YAAYA,YAAYC,YAAYA,aAAed,SAASA;AACxE;AANgBO;AAYT,SAASQ,gBAAgBV,GAAaC,GAAW;AACpD,QAAMU,KAAKX,EAAER,IAAIS,EAAET;AACnB,QAAMoB,KAAKZ,EAAEP,IAAIQ,EAAER;AACnB,SAAOkB,KAAKA,KAAKC,KAAKA;AAC1B;AAJgBF;AAUT,SAASG,SAASb,GAAaC,GAAW;AAC7C,SAAOG,KAAKU,KAAKJ,gBAAgBV,GAAGC,CAAAA,CAAAA;AACxC;AAFgBY;;;AC7QT,IAAME,oBAAN,MAAMA,kBAAAA;EAKT,YAAYC,QAAgC;AAJ3BC;AACAC,kCAAwC,oBAAIC,IAAAA;AAC5CC,oCAAgC,oBAAID,IAAAA;AAGjD,SAAKF,YAAYD,OAAOK;EAC5B;;;;EAMA,IAAIC,QAAgB;AAChB,WAAO,KAAKF,SAASG;EACzB;;;;;EAMAC,OAAOC,MAASC,UAA0B;AACtC,QAAI,KAAKN,SAASO,IAAIF,IAAAA,GAAO;AACzB,WAAKG,OAAOH,MAAMC,QAAAA;AAClB;IACJ;AAEA,UAAMG,UAAU,KAAKC,YAAYJ,QAAAA;AACjC,UAAMK,WAAwB;MAAEN;MAAMC,UAAU;QAAEM,GAAGN,SAASM;QAAGC,GAAGP,SAASO;MAAE;MAAGJ;IAAQ;AAE1F,SAAKT,SAASc,IAAIT,MAAMM,QAAAA;AACxB,SAAKI,WAAWN,SAASE,QAAAA;EAC7B;;;;;EAMAK,OAAOX,MAAkB;AACrB,UAAMM,WAAW,KAAKX,SAASiB,IAAIZ,IAAAA;AACnC,QAAI,CAACM,UAAU;AACX,aAAO;IACX;AAEA,SAAKO,gBAAgBP,SAASF,SAASE,QAAAA;AACvC,SAAKX,SAASmB,OAAOd,IAAAA;AACrB,WAAO;EACX;;;;;EAMAG,OAAOH,MAASe,aAAgC;AAC5C,UAAMT,WAAW,KAAKX,SAASiB,IAAIZ,IAAAA;AACnC,QAAI,CAACM,UAAU;AACX,aAAO;IACX;AAEA,UAAMU,aAAa,KAAKX,YAAYU,WAAAA;AAEpC,QAAIC,eAAeV,SAASF,SAAS;AACjC,WAAKS,gBAAgBP,SAASF,SAASE,QAAAA;AACvCA,eAASF,UAAUY;AACnB,WAAKN,WAAWM,YAAYV,QAAAA;IAChC;AAEAA,aAASL,WAAW;MAAEM,GAAGQ,YAAYR;MAAGC,GAAGO,YAAYP;IAAE;AACzD,WAAO;EACX;;;;;EAMAS,QAAc;AACV,SAAKxB,OAAOwB,MAAK;AACjB,SAAKtB,SAASsB,MAAK;EACvB;;;;;EAMAC,SAAc;AACV,WAAOC,MAAMC,KAAK,KAAKzB,SAAS0B,KAAI,CAAA;EACxC;;;;;;;;EAUAC,aAAaC,QAAkBC,QAAgBC,QAAgC;AAC3E,UAAMC,UAAe,CAAA;AACrB,UAAMC,WAAWH,SAASA;AAC1B,UAAMI,SAASC,uBAAuBN,QAAQC,MAAAA;AAE9C,SAAKM,iBAAiBF,QAAQ,CAACtB,aAAAA;AAC3B,YAAMyB,SAASC,gBAAgBT,QAAQjB,SAASL,QAAQ;AACxD,UAAI8B,UAAUJ,UAAU;AACpB,YAAI,CAACF,UAAUA,OAAOnB,SAASN,IAAI,GAAG;AAClC0B,kBAAQO,KAAK3B,SAASN,IAAI;QAC9B;MACJ;IACJ,CAAA;AAEA,WAAO0B;EACX;;;;;EAMAQ,WAAWN,QAAiBH,QAAgC;AACxD,UAAMC,UAAe,CAAA;AAErB,SAAKI,iBAAiBF,QAAQ,CAACtB,aAAAA;AAC3B,YAAM6B,MAAM7B,SAASL;AACrB,UAAIkC,IAAI5B,KAAKqB,OAAOQ,QAAQD,IAAI5B,KAAKqB,OAAOS,QACxCF,IAAI3B,KAAKoB,OAAOU,QAAQH,IAAI3B,KAAKoB,OAAOW,MAAM;AAC9C,YAAI,CAACd,UAAUA,OAAOnB,SAASN,IAAI,GAAG;AAClC0B,kBAAQO,KAAK3B,SAASN,IAAI;QAC9B;MACJ;IACJ,CAAA;AAEA,WAAO0B;EACX;;;;;EAMAc,YAAYjB,QAAkBkB,aAAsBhB,QAAqC;AACrF,QAAIiB,UAAoB;AACxB,QAAIC,gBAAgBF,gBAAgBG,SAAYH,cAAcA,cAAcI;AAE5E,UAAMC,eAAeL,eAAe,KAAKjD,YAAY;AACrD,UAAMoC,SAASC,uBAAuBN,QAAQuB,YAAAA;AAE9C,SAAKhB,iBAAiBF,QAAQ,CAACtB,aAAAA;AAC3B,YAAMyB,SAASC,gBAAgBT,QAAQjB,SAASL,QAAQ;AACxD,UAAI8B,SAASY,eAAe;AACxB,YAAI,CAAClB,UAAUA,OAAOnB,SAASN,IAAI,GAAG;AAClC0C,oBAAUpC,SAASN;AACnB2C,0BAAgBZ;QACpB;MACJ;IACJ,CAAA;AAEA,WAAOW;EACX;;;;;EAMAK,aAAaxB,QAAkByB,GAAWP,aAAsBhB,QAAgC;AAC5F,QAAIuB,KAAK,EAAG,QAAO,CAAA;AAEnB,UAAMC,aAAiD,CAAA;AACvD,UAAMC,YAAYT,gBAAgBG,SAAYH,cAAcA,cAAcI;AAC1E,UAAMC,eAAeL,eAAe,KAAKjD,YAAY;AACrD,UAAMoC,SAASC,uBAAuBN,QAAQuB,YAAAA;AAE9C,SAAKhB,iBAAiBF,QAAQ,CAACtB,aAAAA;AAC3B,YAAMyB,SAASC,gBAAgBT,QAAQjB,SAASL,QAAQ;AACxD,UAAI8B,UAAUmB,WAAW;AACrB,YAAI,CAACzB,UAAUA,OAAOnB,SAASN,IAAI,GAAG;AAClCiD,qBAAWhB,KAAK;YAAEjC,MAAMM,SAASN;YAAM+B;UAAO,CAAA;QAClD;MACJ;IACJ,CAAA;AAEAkB,eAAWE,KAAK,CAACC,GAAGC,MAAMD,EAAErB,SAASsB,EAAEtB,MAAM;AAC7C,WAAOkB,WAAWK,MAAM,GAAGN,CAAAA,EAAGO,IAAIC,CAAAA,MAAKA,EAAExD,IAAI;EACjD;;;;;EAMAyD,QAAQC,QAAkBC,WAAqBlB,aAAqBhB,QAA6C;AAC7G,UAAMmC,OAAyB,CAAA;AAC/B,UAAMC,YAAY,KAAKC,cAAcJ,QAAQC,WAAWlB,WAAAA;AAExD,SAAKX,iBAAiB+B,WAAW,CAACvD,aAAAA;AAC9B,YAAMyD,MAAM,KAAKC,oBAAoBN,QAAQC,WAAWrD,SAASL,UAAUwC,WAAAA;AAC3E,UAAIsB,OAAOA,IAAIE,YAAYxB,aAAa;AACpC,YAAI,CAAChB,UAAUA,OAAOnB,SAASN,IAAI,GAAG;AAClC4D,eAAK3B,KAAK;YACNiC,QAAQ5D,SAASN;YACjBmE,OAAOJ,IAAII;YACXC,QAAQL,IAAIK;YACZH,UAAUF,IAAIE;UAClB,CAAA;QACJ;MACJ;IACJ,CAAA;AAEAL,SAAKT,KAAK,CAACC,GAAGC,MAAMD,EAAEa,WAAWZ,EAAEY,QAAQ;AAC3C,WAAOL;EACX;;;;;EAMAS,aAAaX,QAAkBC,WAAqBlB,aAAqBhB,QAAkD;AACvH,UAAMmC,OAAO,KAAKH,QAAQC,QAAQC,WAAWlB,aAAahB,MAAAA;AAC1D,WAAOmC,KAAKU,SAAS,IAAIV,KAAK,CAAA,IAAK;EACvC;;;;EAMQvD,YAAYJ,UAA4B;AAC5C,UAAMsE,QAAQC,KAAKC,MAAMxE,SAASM,IAAI,KAAKf,SAAS;AACpD,UAAMkF,QAAQF,KAAKC,MAAMxE,SAASO,IAAI,KAAKhB,SAAS;AACpD,WAAO,GAAG+E,KAAAA,IAASG,KAAAA;EACvB;EAEQC,eAAe1E,UAA8C;AACjE,WAAO;MACHM,GAAGiE,KAAKC,MAAMxE,SAASM,IAAI,KAAKf,SAAS;MACzCgB,GAAGgE,KAAKC,MAAMxE,SAASO,IAAI,KAAKhB,SAAS;IAC7C;EACJ;EAEQkB,WAAWN,SAAiBE,UAA6B;AAC7D,QAAIsE,OAAO,KAAKnF,OAAOmB,IAAIR,OAAAA;AAC3B,QAAI,CAACwE,MAAM;AACPA,aAAO,oBAAIC,IAAAA;AACX,WAAKpF,OAAOgB,IAAIL,SAASwE,IAAAA;IAC7B;AACAA,SAAKE,IAAIxE,QAAAA;EACb;EAEQO,gBAAgBT,SAAiBE,UAA6B;AAClE,UAAMsE,OAAO,KAAKnF,OAAOmB,IAAIR,OAAAA;AAC7B,QAAIwE,MAAM;AACNA,WAAK9D,OAAOR,QAAAA;AACZ,UAAIsE,KAAK9E,SAAS,GAAG;AACjB,aAAKL,OAAOqB,OAAOV,OAAAA;MACvB;IACJ;EACJ;EAEQ0B,iBAAiBF,QAAiBmD,UAA6C;AACnF,UAAMC,UAAU,KAAKL,eAAe;MAAEpE,GAAGqB,OAAOQ;MAAM5B,GAAGoB,OAAOU;IAAK,CAAA;AACrE,UAAM2C,UAAU,KAAKN,eAAe;MAAEpE,GAAGqB,OAAOS;MAAM7B,GAAGoB,OAAOW;IAAK,CAAA;AAErE,aAAShC,IAAIyE,QAAQzE,GAAGA,KAAK0E,QAAQ1E,GAAGA,KAAK;AACzC,eAASC,IAAIwE,QAAQxE,GAAGA,KAAKyE,QAAQzE,GAAGA,KAAK;AACzC,cAAMoE,OAAO,KAAKnF,OAAOmB,IAAI,GAAGL,CAAAA,IAAKC,CAAAA,EAAG;AACxC,YAAIoE,MAAM;AACN,qBAAWtE,YAAYsE,MAAM;AACzBG,qBAASzE,QAAAA;UACb;QACJ;MACJ;IACJ;EACJ;EAEQwD,cAAcJ,QAAkBC,WAAqBlB,aAA8B;AACvF,UAAMyC,OAAOxB,OAAOnD,IAAIoD,UAAUpD,IAAIkC;AACtC,UAAM0C,OAAOzB,OAAOlD,IAAImD,UAAUnD,IAAIiC;AAEtC,WAAO;MACHL,MAAMoC,KAAKY,IAAI1B,OAAOnD,GAAG2E,IAAAA;MACzB5C,MAAMkC,KAAKY,IAAI1B,OAAOlD,GAAG2E,IAAAA;MACzB9C,MAAMmC,KAAKa,IAAI3B,OAAOnD,GAAG2E,IAAAA;MACzB3C,MAAMiC,KAAKa,IAAI3B,OAAOlD,GAAG2E,IAAAA;IAC7B;EACJ;EAEQnB,oBACJN,QACAC,WACAQ,OACAmB,cACAC,YAAoB,KAC0C;AAC9D,UAAMC,UAAU;MAAEjF,GAAG4D,MAAM5D,IAAImD,OAAOnD;MAAGC,GAAG2D,MAAM3D,IAAIkD,OAAOlD;IAAE;AAC/D,UAAMiF,aAAaD,QAAQjF,IAAIoD,UAAUpD,IAAIiF,QAAQhF,IAAImD,UAAUnD;AAEnE,QAAIiF,aAAa,GAAG;AAChB,aAAO;IACX;AAEA,UAAMC,WAAWhC,OAAOnD,IAAIoD,UAAUpD,IAAIkF;AAC1C,UAAME,WAAWjC,OAAOlD,IAAImD,UAAUnD,IAAIiF;AAC1C,UAAMG,aAAapB,KAAKqB,MACnB1B,MAAM5D,IAAImF,aAAavB,MAAM5D,IAAImF,aACjCvB,MAAM3D,IAAImF,aAAaxB,MAAM3D,IAAImF,SAAO;AAG7C,QAAIC,cAAcL,WAAW;AACzB,YAAMO,UAAUL,aAAajB,KAAKqB,KAAKN,YAAYA,YAAYK,aAAaA,UAAAA;AAC5E,UAAIE,WAAW,GAAG;AACd,cAAMC,WAAW;UACbxF,GAAGmD,OAAOnD,IAAIoD,UAAUpD,IAAIuF;UAC5BtF,GAAGkD,OAAOlD,IAAImD,UAAUnD,IAAIsF;QAChC;AACA,cAAM1B,SAAS;UACX7D,GAAGwF,SAASxF,IAAI4D,MAAM5D;UACtBC,GAAGuF,SAASvF,IAAI2D,MAAM3D;QAC1B;AACA,cAAMwF,YAAYxB,KAAKqB,KAAKzB,OAAO7D,IAAI6D,OAAO7D,IAAI6D,OAAO5D,IAAI4D,OAAO5D,CAAC;AACrE,YAAIwF,YAAY,GAAG;AACf5B,iBAAO7D,KAAKyF;AACZ5B,iBAAO5D,KAAKwF;QAChB;AACA,eAAO;UAAE7B,OAAO4B;UAAU3B;UAAQH,UAAU6B;QAAQ;MACxD;IACJ;AAEA,WAAO;EACX;AACJ;AApUaxG;AAAN,IAAMA,mBAAN;AA8UA,SAAS2G,uBAA0BrG,WAAmB,KAAG;AAC5D,SAAO,IAAIN,iBAAoB;IAAEM;EAAS,CAAA;AAC9C;AAFgBqG;;;ACxUT,IAAMC,WAAN,MAAMA,SAAAA;EAMT,YAAYC,QAAuB;AALlBC;AACAC,kCAA+C,oBAAIC,IAAAA;AACnDC,sCAAyC,oBAAID,IAAAA;AAC7CE,4CAA6C,oBAAIC,IAAAA;AAG9D,SAAKL,YAAYD,OAAOO;EAC5B;;;;EAMA,IAAIC,QAAgB;AAChB,WAAO,KAAKJ,WAAWK;EAC3B;;;;;EAMAC,YAAYC,QAAWC,UAAoBZ,QAAkC;AACzE,QAAI,KAAKI,WAAWS,IAAIF,MAAAA,GAAS;AAC7B,WAAKG,eAAeH,QAAQC,QAAAA;AAC5B,WAAKG,gBAAgBJ,QAAQX,OAAOgB,SAAS;AAC7C;IACJ;AAEA,UAAMC,UAAU,KAAKC,YAAYN,QAAAA;AACjC,UAAMO,OAA2B;MAC7BR;MACAC,UAAU;QAAEQ,GAAGR,SAASQ;QAAGC,GAAGT,SAASS;MAAE;MACzCL,WAAWhB,OAAOgB;MAClBM,aAAatB,OAAOgB,YAAYhB,OAAOgB;MACvCO,YAAYvB,OAAOuB,eAAe;MAClCN;MACAO,iBAAiB,oBAAIlB,IAAAA;MACrBmB,WAAW,oBAAInB,IAAAA;IACnB;AAEA,SAAKF,WAAWsB,IAAIf,QAAQQ,IAAAA;AAC5B,SAAKQ,WAAWV,SAASE,IAAAA;AAGzB,SAAKS,kBAAkBT,IAAAA;EAC3B;;;;;EAMAU,eAAelB,QAAoB;AAC/B,UAAMQ,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAI,CAACQ,MAAM;AACP,aAAO;IACX;AAGA,QAAIA,KAAKI,YAAY;AACjB,iBAAW,CAAA,EAAGQ,SAAAA,KAAc,KAAK3B,YAAY;AACzC,YAAI2B,cAAcZ,QAAQY,UAAUP,gBAAgBX,IAAIF,MAAAA,GAAS;AAC7DoB,oBAAUP,gBAAgBQ,OAAOrB,MAAAA;AACjC,eAAKsB,WAAW;YACZC,MAAM;YACNC,UAAUJ,UAAUpB;YACpByB,QAAQzB;YACRC,UAAUO,KAAKP;UACnB,GAAGmB,SAAAA;QACP;MACJ;IACJ;AAGA,eAAWM,WAAWlB,KAAKK,iBAAiB;AACxC,YAAMc,cAAc,KAAKlC,WAAW0B,IAAIO,OAAAA;AACxC,UAAIC,aAAa;AACb,aAAKL,WAAW;UACZC,MAAM;UACNC,UAAUxB;UACVyB,QAAQC;UACRzB,UAAU0B,YAAY1B;QAC1B,GAAGO,IAAAA;MACP;IACJ;AAEA,SAAKoB,gBAAgBpB,KAAKF,SAASE,IAAAA;AACnC,SAAKf,WAAW4B,OAAOrB,MAAAA;AACvB,WAAO;EACX;;;;;EAMAG,eAAeH,QAAW6B,aAAgC;AACtD,UAAMrB,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAI,CAACQ,MAAM;AACP,aAAO;IACX;AAEA,UAAMsB,aAAa,KAAKvB,YAAYsB,WAAAA;AAGpC,QAAIC,eAAetB,KAAKF,SAAS;AAC7B,WAAKsB,gBAAgBpB,KAAKF,SAASE,IAAAA;AACnCA,WAAKF,UAAUwB;AACf,WAAKd,WAAWc,YAAYtB,IAAAA;IAChC;AAEAA,SAAKP,WAAW;MAAEQ,GAAGoB,YAAYpB;MAAGC,GAAGmB,YAAYnB;IAAE;AAGrD,SAAKO,kBAAkBT,IAAAA;AAGvB,QAAIA,KAAKI,YAAY;AACjB,WAAKmB,yBAAyBvB,IAAAA;IAClC;AAEA,WAAO;EACX;;;;;EAMAJ,gBAAgBJ,QAAWgC,UAA2B;AAClD,UAAMxB,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAI,CAACQ,MAAM;AACP,aAAO;IACX;AAEAA,SAAKH,YAAY2B;AACjBxB,SAAKG,cAAcqB,WAAWA;AAG9B,SAAKf,kBAAkBT,IAAAA;AAEvB,WAAO;EACX;;;;;EAMAyB,kBAAkBjC,QAAgB;AAC9B,UAAMQ,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAI,CAACQ,MAAM;AACP,aAAO,CAAA;IACX;AACA,WAAO0B,MAAMC,KAAK3B,KAAKK,eAAe;EAC1C;;;;;EAMAuB,eAAepC,QAAgB;AAC3B,UAAMQ,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAI,CAACQ,QAAQ,CAACA,KAAKI,YAAY;AAC3B,aAAO,CAAA;IACX;AAEA,UAAMyB,YAAiB,CAAA;AACvB,eAAW,CAAA,EAAGjB,SAAAA,KAAc,KAAK3B,YAAY;AACzC,UAAI2B,cAAcZ,QAAQY,UAAUP,gBAAgBX,IAAIF,MAAAA,GAAS;AAC7DqC,kBAAUC,KAAKlB,UAAUpB,MAAM;MACnC;IACJ;AACA,WAAOqC;EACX;;;;;EAMAE,OAAOf,UAAaC,QAAoB;AACpC,UAAMjB,OAAO,KAAKf,WAAW0B,IAAIK,QAAAA;AACjC,QAAI,CAAChB,MAAM;AACP,aAAO;IACX;AACA,WAAOA,KAAKK,gBAAgBX,IAAIuB,MAAAA;EACpC;;;;;EAMAe,YAAYC,UAAqC;AAC7C,SAAK/C,iBAAiBgD,IAAID,QAAAA;EAC9B;;;;;EAMAE,eAAeF,UAAqC;AAChD,SAAK/C,iBAAiB2B,OAAOoB,QAAAA;EACjC;;;;;EAMAG,kBAAkB5C,QAAWyC,UAAqC;AAC9D,UAAMjC,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAIQ,MAAM;AACNA,WAAKM,UAAU4B,IAAID,QAAAA;IACvB;EACJ;;;;;EAMAI,qBAAqB7C,QAAWyC,UAAqC;AACjE,UAAMjC,OAAO,KAAKf,WAAW0B,IAAInB,MAAAA;AACjC,QAAIQ,MAAM;AACNA,WAAKM,UAAUO,OAAOoB,QAAAA;IAC1B;EACJ;;;;;EAMAK,QAAc;AACV,SAAKvD,OAAOuD,MAAK;AACjB,SAAKrD,WAAWqD,MAAK;EACzB;;;;EAMQvC,YAAYN,UAA4B;AAC5C,UAAM8C,QAAQC,KAAKC,MAAMhD,SAASQ,IAAI,KAAKnB,SAAS;AACpD,UAAM4D,QAAQF,KAAKC,MAAMhD,SAASS,IAAI,KAAKpB,SAAS;AACpD,WAAO,GAAGyD,KAAAA,IAASG,KAAAA;EACvB;EAEQC,eAAelD,UAA8C;AACjE,WAAO;MACHQ,GAAGuC,KAAKC,MAAMhD,SAASQ,IAAI,KAAKnB,SAAS;MACzCoB,GAAGsC,KAAKC,MAAMhD,SAASS,IAAI,KAAKpB,SAAS;IAC7C;EACJ;EAEQ0B,WAAWV,SAAiBE,MAAgC;AAChE,QAAI4C,OAAO,KAAK7D,OAAO4B,IAAIb,OAAAA;AAC3B,QAAI,CAAC8C,MAAM;AACPA,aAAO,oBAAIzD,IAAAA;AACX,WAAKJ,OAAOwB,IAAIT,SAAS8C,IAAAA;IAC7B;AACAA,SAAKV,IAAIlC,IAAAA;EACb;EAEQoB,gBAAgBtB,SAAiBE,MAAgC;AACrE,UAAM4C,OAAO,KAAK7D,OAAO4B,IAAIb,OAAAA;AAC7B,QAAI8C,MAAM;AACNA,WAAK/B,OAAOb,IAAAA;AACZ,UAAI4C,KAAKtD,SAAS,GAAG;AACjB,aAAKP,OAAO8B,OAAOf,OAAAA;MACvB;IACJ;EACJ;;;;;EAMQW,kBAAkBT,MAAgC;AACtD,UAAM6C,aAAa,oBAAI1D,IAAAA;AAGvB,UAAM2D,aAAaN,KAAKO,KAAK/C,KAAKH,YAAY,KAAKf,SAAS;AAC5D,UAAMkE,aAAa,KAAKL,eAAe3C,KAAKP,QAAQ;AAGpD,aAASwD,KAAK,CAACH,YAAYG,MAAMH,YAAYG,MAAM;AAC/C,eAASC,KAAK,CAACJ,YAAYI,MAAMJ,YAAYI,MAAM;AAC/C,cAAMpD,UAAU,GAAGkD,WAAW/C,IAAIgD,EAAAA,IAAMD,WAAW9C,IAAIgD,EAAAA;AACvD,cAAMN,OAAO,KAAK7D,OAAO4B,IAAIb,OAAAA;AAC7B,YAAI,CAAC8C,KAAM;AAEX,mBAAWhC,aAAagC,MAAM;AAC1B,cAAIhC,cAAcZ,KAAM;AACxB,cAAI,CAACY,UAAUR,WAAY;AAE3B,gBAAM+C,SAASC,gBAAgBpD,KAAKP,UAAUmB,UAAUnB,QAAQ;AAChE,cAAI0D,UAAUnD,KAAKG,aAAa;AAC5B0C,uBAAWX,IAAItB,UAAUpB,MAAM;UACnC;QACJ;MACJ;IACJ;AAGA,eAAWA,UAAUqD,YAAY;AAC7B,UAAI,CAAC7C,KAAKK,gBAAgBX,IAAIF,MAAAA,GAAS;AACnC,cAAM6D,aAAa,KAAKpE,WAAW0B,IAAInB,MAAAA;AACvC,YAAI6D,YAAY;AACZ,eAAKvC,WAAW;YACZC,MAAM;YACNC,UAAUhB,KAAKR;YACfyB,QAAQzB;YACRC,UAAU4D,WAAW5D;UACzB,GAAGO,IAAAA;QACP;MACJ;IACJ;AAGA,eAAWR,UAAUQ,KAAKK,iBAAiB;AACvC,UAAI,CAACwC,WAAWnD,IAAIF,MAAAA,GAAS;AACzB,cAAM6D,aAAa,KAAKpE,WAAW0B,IAAInB,MAAAA;AACvC,cAAMC,WAAW4D,YAAY5D,YAAY;UAAEQ,GAAG;UAAGC,GAAG;QAAE;AACtD,aAAKY,WAAW;UACZC,MAAM;UACNC,UAAUhB,KAAKR;UACfyB,QAAQzB;UACRC;QACJ,GAAGO,IAAAA;MACP;IACJ;AAEAA,SAAKK,kBAAkBwC;EAC3B;;;;;EAMQtB,yBAAyB+B,WAAqC;AAClE,UAAMR,aAAaN,KAAKO,KAAK,KAAKQ,iBAAgB,IAAK,KAAKzE,SAAS,IAAI;AACzE,UAAMkE,aAAa,KAAKL,eAAeW,UAAU7D,QAAQ;AAEzD,aAASwD,KAAK,CAACH,YAAYG,MAAMH,YAAYG,MAAM;AAC/C,eAASC,KAAK,CAACJ,YAAYI,MAAMJ,YAAYI,MAAM;AAC/C,cAAMpD,UAAU,GAAGkD,WAAW/C,IAAIgD,EAAAA,IAAMD,WAAW9C,IAAIgD,EAAAA;AACvD,cAAMN,OAAO,KAAK7D,OAAO4B,IAAIb,OAAAA;AAC7B,YAAI,CAAC8C,KAAM;AAEX,mBAAWhC,aAAagC,MAAM;AAC1B,cAAIhC,cAAc0C,UAAW;AAE7B,gBAAMH,SAASC,gBAAgBxC,UAAUnB,UAAU6D,UAAU7D,QAAQ;AACrE,gBAAM+D,aAAa5C,UAAUP,gBAAgBX,IAAI4D,UAAU9D,MAAM;AACjE,gBAAMiE,YAAYN,UAAUvC,UAAUT;AAEtC,cAAIsD,aAAa,CAACD,YAAY;AAC1B5C,sBAAUP,gBAAgB6B,IAAIoB,UAAU9D,MAAM;AAC9C,iBAAKsB,WAAW;cACZC,MAAM;cACNC,UAAUJ,UAAUpB;cACpByB,QAAQqC,UAAU9D;cAClBC,UAAU6D,UAAU7D;YACxB,GAAGmB,SAAAA;UACP,WAAW,CAAC6C,aAAaD,YAAY;AACjC5C,sBAAUP,gBAAgBQ,OAAOyC,UAAU9D,MAAM;AACjD,iBAAKsB,WAAW;cACZC,MAAM;cACNC,UAAUJ,UAAUpB;cACpByB,QAAQqC,UAAU9D;cAClBC,UAAU6D,UAAU7D;YACxB,GAAGmB,SAAAA;UACP;QACJ;MACJ;IACJ;EACJ;;;;;EAMQ2C,mBAA2B;AAC/B,QAAIG,MAAM;AACV,eAAW,CAAA,EAAG1D,IAAAA,KAAS,KAAKf,YAAY;AACpC,UAAIe,KAAKH,YAAY6D,KAAK;AACtBA,cAAM1D,KAAKH;MACf;IACJ;AACA,WAAO6D;EACX;;;;;EAMQ5C,WAAW6C,OAAqBC,cAAwC;AAE5E,eAAW3B,YAAY2B,aAAatD,WAAW;AAC3C,UAAI;AACA2B,iBAAS0B,KAAAA;MACb,SAASE,GAAG;AACRC,gBAAQC,MAAM,8BAA8BF,CAAAA;MAChD;IACJ;AAGA,eAAW5B,YAAY,KAAK/C,kBAAkB;AAC1C,UAAI;AACA+C,iBAAS0B,KAAAA;MACb,SAASE,GAAG;AACRC,gBAAQC,MAAM,8BAA8BF,CAAAA;MAChD;IACJ;EACJ;AACJ;AA1ZajF;AAAN,IAAMA,UAAN;AAsaA,SAASoF,cAAiB5E,WAAmB,KAAG;AACnD,SAAO,IAAIR,QAAW;IAAEQ;EAAS,CAAA;AACrC;AAFgB4E;;;AClehB,SAASC,0BAA0B;AAW5B,IAAMC,oBAAoBD,mBAA2C,cAAA;AASrE,IAAME,oBAAoBF,mBAA2C,cAAA;AASrE,IAAMG,kBAAkBH,mBAAyC,YAAA;;;ACDjE,IAAMI,uBAA8C;EACvDC,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAQ;IAAU;IAAS;;EACjDC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAMC,wBAAN,MAAMA,sBAAAA;EACTC,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAME,UAAUD,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMC,UAAUJ,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAME,SAASL,IAAIE,cAAcJ,KAAKK,IAAI,UAAU,GAAA;AAEpD,UAAMG,UAAUN,IAAIO,cAAcC,aAAa;MAAEC,GAAGR;MAASS,GAAGN;IAAQ,GAAGC,MAAAA,KAAW,CAAA;AAEtF,WAAO;MACHX,SAAS;QACLY;QACAK,OAAOL,QAAQM;MACnB;IACJ;EACJ;AACJ;AAhBahB;AAAN,IAAMA,uBAAN;AA0BA,IAAMiB,qBAA4C;EACrD9B,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAQ;IAAQ;IAAa;IAAQ;;EAC3DC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAMmB,sBAAN,MAAMA,oBAAAA;EACTjB,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAMgB,OAAOf,IAAIE,cAAcJ,KAAKK,IAAI,QAAQ,CAAA;AAChD,UAAMa,OAAOhB,IAAIE,cAAcJ,KAAKK,IAAI,QAAQ,CAAA;AAChD,UAAMc,OAAOjB,IAAIE,cAAcJ,KAAKK,IAAI,QAAQ,GAAA;AAChD,UAAMe,OAAOlB,IAAIE,cAAcJ,KAAKK,IAAI,QAAQ,GAAA;AAEhD,UAAMgB,SAAkB;MAAEJ;MAAMC;MAAMC;MAAMC;IAAK;AACjD,UAAMZ,UAAUN,IAAIO,cAAca,WAAWD,MAAAA,KAAW,CAAA;AAExD,WAAO;MACHzB,SAAS;QACLY;QACAK,OAAOL,QAAQM;MACnB;IACJ;EACJ;AACJ;AAlBaE;AAAN,IAAMA,qBAAN;AA4BA,IAAMO,sBAA6C;EACtDtC,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAQ;IAAW;IAAW;;EACpDC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAM2B,uBAAN,MAAMA,qBAAAA;EACTzB,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAME,UAAUD,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMC,UAAUJ,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMoB,cAAcvB,IAAIE,cAAcJ,KAAKK,IAAI,eAAe,GAAA;AAE9D,UAAMqB,SAASxB,IAAIO,cAAckB,YAAY;MAAEhB,GAAGR;MAASS,GAAGN;IAAQ,GAAGmB,WAAAA,KAAgB;AAEzF,WAAO;MACH7B,SAAS;QACL8B;QACAE,OAAOF,WAAW;MACtB;IACJ;EACJ;AACJ;AAhBaF;AAAN,IAAMA,sBAAN;AA0BA,IAAMK,uBAA8C;EACvD5C,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAQ;IAAW;IAAK;;EAC9CC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAMiC,wBAAN,MAAMA,sBAAAA;EACT/B,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAME,UAAUD,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMC,UAAUJ,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAM0B,IAAI7B,IAAIE,cAAcJ,KAAKK,IAAI,KAAK,CAAA;AAC1C,UAAMoB,cAAcvB,IAAIE,cAAcJ,KAAKK,IAAI,eAAe,GAAA;AAE9D,UAAMG,UAAUN,IAAIO,cAAcuB,aAAa;MAAErB,GAAGR;MAASS,GAAGN;IAAQ,GAAGyB,GAAGN,WAAAA,KAAgB,CAAA;AAE9F,WAAO;MACH7B,SAAS;QACLY;QACAK,OAAOL,QAAQM;MACnB;IACJ;EACJ;AACJ;AAjBagB;AAAN,IAAMA,uBAAN;AA2BA,IAAMG,kBAAyC;EAClDhD,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAW;IAAO;IAAO;;EAC/CC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAMqC,mBAAN,MAAMA,iBAAAA;EACTnC,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAMkC,UAAUjC,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAM+B,UAAUlC,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMgC,aAAanC,IAAIE,cAAcJ,KAAKK,IAAI,cAAc,CAAA;AAC5D,UAAMiC,aAAapC,IAAIE,cAAcJ,KAAKK,IAAI,cAAc,CAAA;AAC5D,UAAMoB,cAAcvB,IAAIE,cAAcJ,KAAKK,IAAI,eAAe,GAAA;AAE9D,UAAMkC,OAAOrC,IAAIO,cAAc+B,QAC3B;MAAE7B,GAAGwB;MAASvB,GAAGwB;IAAQ,GACzB;MAAEzB,GAAG0B;MAAYzB,GAAG0B;IAAW,GAC/Bb,WAAAA,KACC,CAAA;AAEL,WAAO;MACH7B,SAAS;QACL2C;QACAE,UAAUF,KAAKzB;QACf4B,QAAQH,KAAKzB,SAAS;MAC1B;IACJ;EACJ;AACJ;AAvBaoB;AAAN,IAAMA,kBAAN;AA6BA,IAAMS,uBAA8C;EACvD1D,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAW;IAAW;IAAO;IAAS;IAAO;;EACxDC,UAAU;IAAC;IAAW;;EACtBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;IACA;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;MACNU,cAAc;IAClB;;EAEJC,SAAS;IACL;MACIH,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJY,OAAO;AACX;AAMO,IAAM+C,wBAAN,MAAMA,sBAAAA;EACT7C,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAMkC,UAAUjC,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAM+B,UAAUlC,IAAIE,cAAcJ,KAAKK,IAAI,WAAW,CAAA;AACtD,UAAMgC,aAAanC,IAAIE,cAAcJ,KAAKK,IAAI,cAAc,CAAA;AAC5D,UAAMiC,aAAapC,IAAIE,cAAcJ,KAAKK,IAAI,cAAc,CAAA;AAC5D,UAAMoB,cAAcvB,IAAIE,cAAcJ,KAAKK,IAAI,eAAe,GAAA;AAE9D,UAAMwC,MAAM3C,IAAIO,cAAcqC,aAC1B;MAAEnC,GAAGwB;MAASvB,GAAGwB;IAAQ,GACzB;MAAEzB,GAAG0B;MAAYzB,GAAG0B;IAAW,GAC/Bb,WAAAA,KACC;AAEL,WAAO;MACH7B,SAAS;QACLiD;QACAE,QAAQF,KAAKE,UAAU;QACvBC,WAAWH,KAAKI,MAAMtC,KAAK;QAC3BuC,WAAWL,KAAKI,MAAMrC,KAAK;QAC3BuC,UAAUN,KAAKM,YAAY;QAC3BT,QAAQG,QAAQ;MACpB;IACJ;EACJ;AACJ;AA1BaD;AAAN,IAAMA,uBAAN;AAoCA,IAAMQ,8BAA8B;EACvC;IAAEC,UAAUrE;IAAsBsE,UAAU,IAAIxD,qBAAAA;EAAuB;EACvE;IAAEuD,UAAUtC;IAAoBuC,UAAU,IAAItC,mBAAAA;EAAqB;EACnE;IAAEqC,UAAU9B;IAAqB+B,UAAU,IAAI9B,oBAAAA;EAAsB;EACrE;IAAE6B,UAAUxB;IAAsByB,UAAU,IAAIxB,qBAAAA;EAAuB;EACvE;IAAEuB,UAAUpB;IAAiBqB,UAAU,IAAIpB,gBAAAA;EAAkB;EAC7D;IAAEmB,UAAUV;IAAsBW,UAAU,IAAIV,qBAAAA;EAAuB;;;;ACxgBpE,IAAMW,4BAAmD;EAC5DC,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAO;IAAQ;IAAY;;EACtCC,UAAU;IAAC;IAAO;;EAClBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJU,SAAS;IACL;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJW,OAAO;AACX;AAMO,IAAMC,6BAAN,MAAMA,2BAAAA;EACTC,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAME,WAAWD,IAAIE,cAAcJ,KAAKK,IAAI,YAAYH,IAAII,MAAM;AAElE,UAAMC,WAAWL,IAAIM,YAAYC,kBAAkBN,QAAAA,KAAa,CAAA;AAEhE,WAAO;MACHP,SAAS;QACLW;QACAG,OAAOH,SAASI;MACpB;IACJ;EACJ;AACJ;AAdab;AAAN,IAAMA,4BAAN;AAwBA,IAAMc,yBAAgD;EACzD1B,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAO;IAAa;IAAY;;EAC3CC,UAAU;IAAC;IAAO;;EAClBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJU,SAAS;IACL;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJW,OAAO;AACX;AAMO,IAAMgB,0BAAN,MAAMA,wBAAAA;EACTd,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAMa,SAASZ,IAAIE,cAAcJ,KAAKK,IAAI,UAAUH,IAAII,MAAM;AAE9D,UAAMS,YAAYb,IAAIM,YAAYQ,eAAeF,MAAAA,KAAW,CAAA;AAE5D,WAAO;MACHlB,SAAS;QACLmB;QACAL,OAAOK,UAAUJ;MACrB;IACJ;EACJ;AACJ;AAdaE;AAAN,IAAMA,yBAAN;AAwBA,IAAMI,iBAAwC;EACjD/B,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAO;IAAc;IAAO;IAAO;;EAC9CC,UAAU;IAAC;IAAO;;EAClBC,QAAQ;EACRC,QAAQ;IACJ;MACIC,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJU,SAAS;IACL;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;EAEJW,OAAO;AACX;AAMO,IAAMqB,kBAAN,MAAMA,gBAAAA;EACTnB,QAAQC,MAAqBC,SAAmC;AAC5D,UAAMC,MAAMD;AACZ,UAAME,WAAWD,IAAIE,cAAcJ,KAAKK,IAAI,YAAYH,IAAII,MAAM;AAClE,UAAMQ,SAASZ,IAAIE,cAAcJ,KAAKK,IAAI,UAAU,IAAA;AAEpD,UAAMc,SAASjB,IAAIM,YAAYW,OAAOhB,UAAUW,MAAAA,KAAW;AAE3D,WAAO;MACHlB,SAAS;QACLuB;MACJ;IACJ;EACJ;AACJ;AAdaD;AAAN,IAAMA,iBAAN;AAwBA,IAAME,4BAAmD;EAC5DlC,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAO;IAAS;IAAS;IAAQ;;EAC5CC,UAAU;IAAC;IAAO;IAAU;;EAC5BM,OAAO;EACPJ,QAAQ,CAAA;EACRG,SAAS;IACL;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;AAER;AAMO,IAAMmC,6BAAN,MAAMA,2BAAAA;EACTtB,QAAQuB,OAAsBC,UAAoC;AAE9D,WAAO;MAAEC,UAAU;IAAO;EAC9B;AACJ;AALaH;AAAN,IAAMA,4BAAN;AAeA,IAAMI,2BAAkD;EAC3DvC,MAAM;EACNC,OAAO;EACPC,UAAU;EACVC,aAAa;EACbC,UAAU;IAAC;IAAO;IAAS;IAAQ;IAAQ;;EAC3CC,UAAU;IAAC;IAAO;IAAU;;EAC5BM,OAAO;EACPJ,QAAQ,CAAA;EACRG,SAAS;IACL;MACIF,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;IACA;MACIQ,MAAM;MACNC,aAAa;MACbT,MAAM;IACV;;AAER;AAMO,IAAMwC,4BAAN,MAAMA,0BAAAA;EACT3B,QAAQuB,OAAsBC,UAAoC;AAE9D,WAAO;MAAEC,UAAU;IAAO;EAC9B;AACJ;AALaE;AAAN,IAAMA,2BAAN;AAeA,IAAMC,qBAAqB;EAC9BC,WAAW;IACP3C;IACA2B;IACAK;IACAG;IACAK;;EAEJI,WAAW,oBAAIC,IAA2B;IACtC;MAAC;MAAqB,IAAIhC,0BAAAA;;IAC1B;MAAC;MAAkB,IAAIe,uBAAAA;;IACvB;MAAC;MAAU,IAAIK,eAAAA;;IACf;MAAC;MAAwB,IAAIG,0BAAAA;;IAC7B;MAAC;MAAuB,IAAIK,yBAAAA;;GAC/B;AACL;","names":["createBounds","minX","minY","maxX","maxY","createBoundsFromCenter","center","width","height","halfWidth","halfHeight","x","y","createBoundsFromCircle","radius","isPointInBounds","point","bounds","boundsIntersect","a","b","boundsIntersectsCircle","closestX","Math","max","min","closestY","distanceX","distanceY","distanceSquared","dx","dy","distance","sqrt","GridSpatialIndex","config","_cellSize","_cells","Map","_itemMap","cellSize","count","size","insert","item","position","has","update","cellKey","_getCellKey","gridItem","x","y","set","_addToCell","remove","get","_removeFromCell","delete","newPosition","newCellKey","clear","getAll","Array","from","keys","findInRadius","center","radius","filter","results","radiusSq","bounds","createBoundsFromCircle","_forEachInBounds","distSq","distanceSquared","push","findInRect","pos","minX","maxX","minY","maxY","findNearest","maxDistance","nearest","nearestDistSq","undefined","Infinity","searchRadius","findKNearest","k","candidates","maxDistSq","sort","a","b","slice","map","c","raycast","origin","direction","hits","rayBounds","_getRayBounds","hit","_rayIntersectsPoint","distance","target","point","normal","raycastFirst","length","cellX","Math","floor","cellY","_getCellCoords","cell","Set","add","callback","minCell","maxCell","endX","endY","min","max","_maxDistance","hitRadius","toPoint","projection","closestX","closestY","distToLine","sqrt","hitDist","hitPoint","normalLen","createGridSpatialIndex","GridAOI","config","_cellSize","_cells","Map","_observers","_globalListeners","Set","cellSize","count","size","addObserver","entity","position","has","updatePosition","updateViewRange","viewRange","cellKey","_getCellKey","data","x","y","viewRangeSq","observable","visibleEntities","listeners","set","_addToCell","_updateVisibility","removeObserver","get","otherData","delete","_emitEvent","type","observer","target","visible","visibleData","_removeFromCell","newPosition","newCellKey","_updateObserversOfEntity","newRange","getEntitiesInView","Array","from","getObserversOf","observers","push","canSee","addListener","listener","add","removeListener","addEntityListener","removeEntityListener","clear","cellX","Math","floor","cellY","_getCellCoords","cell","newVisible","cellRadius","ceil","centerCell","dx","dy","distSq","distanceSquared","targetData","movedData","_getMaxViewRange","wasVisible","isVisible","max","event","observerData","e","console","error","createGridAOI","createServiceToken","SpatialIndexToken","SpatialQueryToken","AOIManagerToken","FindInRadiusTemplate","type","title","category","description","keywords","menuPath","isPure","inputs","name","displayName","defaultValue","outputs","color","FindInRadiusExecutor","execute","node","context","ctx","centerX","evaluateInput","id","centerY","radius","results","spatialQuery","findInRadius","x","y","count","length","FindInRectTemplate","FindInRectExecutor","minX","minY","maxX","maxY","bounds","findInRect","FindNearestTemplate","FindNearestExecutor","maxDistance","result","findNearest","found","FindKNearestTemplate","FindKNearestExecutor","k","findKNearest","RaycastTemplate","RaycastExecutor","originX","originY","directionX","directionY","hits","raycast","hitCount","hasHit","RaycastFirstTemplate","RaycastFirstExecutor","hit","raycastFirst","target","hitPointX","point","hitPointY","distance","SpatialQueryNodeDefinitions","template","executor","GetEntitiesInViewTemplate","type","title","category","description","keywords","menuPath","isPure","inputs","name","displayName","outputs","color","GetEntitiesInViewExecutor","execute","node","context","ctx","observer","evaluateInput","id","entity","entities","aoiManager","getEntitiesInView","count","length","GetObserversOfTemplate","GetObserversOfExecutor","target","observers","getObserversOf","CanSeeTemplate","CanSeeExecutor","canSee","OnEntityEnterViewTemplate","OnEntityEnterViewExecutor","_node","_context","nextExec","OnEntityExitViewTemplate","OnEntityExitViewExecutor","AOINodeDefinitions","templates","executors","Map"]}
1
+ {"version":3,"sources":["../src/ISpatialQuery.ts","../src/GridSpatialIndex.ts","../src/aoi/GridAOI.ts","../src/tokens.ts","../src/nodes/SpatialQueryNodes.ts","../src/aoi/AOINodes.ts"],"sourcesContent":["/**\n * @zh 空间查询接口\n * @en Spatial Query Interface\n *\n * @zh 提供空间查询的核心抽象\n * @en Provides core abstractions for spatial queries\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\n\n// =============================================================================\n// 基础类型 | Basic Types\n// =============================================================================\n\n/**\n * @zh 空间边界框\n * @en Spatial bounding box\n */\nexport interface IBounds {\n /**\n * @zh 最小 X 坐标\n * @en Minimum X coordinate\n */\n readonly minX: number;\n\n /**\n * @zh 最小 Y 坐标\n * @en Minimum Y coordinate\n */\n readonly minY: number;\n\n /**\n * @zh 最大 X 坐标\n * @en Maximum X coordinate\n */\n readonly maxX: number;\n\n /**\n * @zh 最大 Y 坐标\n * @en Maximum Y coordinate\n */\n readonly maxY: number;\n}\n\n/**\n * @zh 可定位对象接口\n * @en Positionable object interface\n */\nexport interface IPositionable {\n /**\n * @zh 获取对象位置\n * @en Get object position\n */\n readonly position: IVector2;\n}\n\n/**\n * @zh 可定位且有边界的对象接口\n * @en Positionable object with bounds interface\n */\nexport interface IBoundable extends IPositionable {\n /**\n * @zh 获取对象边界\n * @en Get object bounds\n */\n readonly bounds: IBounds;\n}\n\n/**\n * @zh 射线检测结果\n * @en Raycast hit result\n */\nexport interface IRaycastHit<T> {\n /**\n * @zh 命中的对象\n * @en Hit object\n */\n readonly target: T;\n\n /**\n * @zh 命中点\n * @en Hit point\n */\n readonly point: IVector2;\n\n /**\n * @zh 命中点的法线\n * @en Normal at hit point\n */\n readonly normal: IVector2;\n\n /**\n * @zh 距离射线起点的距离\n * @en Distance from ray origin\n */\n readonly distance: number;\n}\n\n/**\n * @zh 过滤器函数类型\n * @en Filter function type\n */\nexport type SpatialFilter<T> = (item: T) => boolean;\n\n// =============================================================================\n// 空间查询接口 | Spatial Query Interface\n// =============================================================================\n\n/**\n * @zh 空间查询接口\n * @en Spatial query interface\n *\n * @zh 提供空间查询能力,支持范围查询、最近邻查询和射线检测\n * @en Provides spatial query capabilities including range queries, nearest neighbor queries, and raycasting\n */\nexport interface ISpatialQuery<T> {\n /**\n * @zh 查找半径内的所有对象\n * @en Find all objects within radius\n *\n * @param center - @zh 中心点 @en Center point\n * @param radius - @zh 半径 @en Radius\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 半径内的对象数组 @en Array of objects within radius\n */\n findInRadius(center: IVector2, radius: number, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 查找矩形区域内的所有对象\n * @en Find all objects within rectangle\n *\n * @param bounds - @zh 边界框 @en Bounding box\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 区域内的对象数组 @en Array of objects within bounds\n */\n findInRect(bounds: IBounds, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 查找最近的对象\n * @en Find nearest object\n *\n * @param center - @zh 查询中心点 @en Query center point\n * @param maxDistance - @zh 最大搜索距离 @en Maximum search distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 最近的对象,没有则返回 null @en Nearest object or null\n */\n findNearest(center: IVector2, maxDistance?: number, filter?: SpatialFilter<T>): T | null;\n\n /**\n * @zh 查找最近的 K 个对象\n * @en Find K nearest objects\n *\n * @param center - @zh 查询中心点 @en Query center point\n * @param k - @zh 返回的对象数量 @en Number of objects to return\n * @param maxDistance - @zh 最大搜索距离 @en Maximum search distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 最近的 K 个对象数组 @en Array of K nearest objects\n */\n findKNearest(center: IVector2, k: number, maxDistance?: number, filter?: SpatialFilter<T>): T[];\n\n /**\n * @zh 射线检测\n * @en Raycast\n *\n * @param origin - @zh 射线起点 @en Ray origin\n * @param direction - @zh 射线方向(应归一化)@en Ray direction (should be normalized)\n * @param maxDistance - @zh 最大检测距离 @en Maximum detection distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 命中结果数组,按距离排序 @en Array of hit results sorted by distance\n */\n raycast(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T>[];\n\n /**\n * @zh 射线检测(仅返回第一个命中)\n * @en Raycast (return first hit only)\n *\n * @param origin - @zh 射线起点 @en Ray origin\n * @param direction - @zh 射线方向(应归一化)@en Ray direction (should be normalized)\n * @param maxDistance - @zh 最大检测距离 @en Maximum detection distance\n * @param filter - @zh 过滤函数 @en Filter function\n * @returns @zh 第一个命中结果,没有则返回 null @en First hit result or null\n */\n raycastFirst(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T> | null;\n}\n\n// =============================================================================\n// 空间索引接口 | Spatial Index Interface\n// =============================================================================\n\n/**\n * @zh 空间索引接口\n * @en Spatial index interface\n *\n * @zh 提供空间索引的管理能力\n * @en Provides spatial index management capabilities\n */\nexport interface ISpatialIndex<T> extends ISpatialQuery<T> {\n /**\n * @zh 插入对象\n * @en Insert object\n *\n * @param item - @zh 要插入的对象 @en Object to insert\n * @param position - @zh 对象位置 @en Object position\n */\n insert(item: T, position: IVector2): void;\n\n /**\n * @zh 移除对象\n * @en Remove object\n *\n * @param item - @zh 要移除的对象 @en Object to remove\n * @returns @zh 是否成功移除 @en Whether removal was successful\n */\n remove(item: T): boolean;\n\n /**\n * @zh 更新对象位置\n * @en Update object position\n *\n * @param item - @zh 要更新的对象 @en Object to update\n * @param newPosition - @zh 新位置 @en New position\n * @returns @zh 是否成功更新 @en Whether update was successful\n */\n update(item: T, newPosition: IVector2): boolean;\n\n /**\n * @zh 清空索引\n * @en Clear index\n */\n clear(): void;\n\n /**\n * @zh 获取索引中的对象数量\n * @en Get number of objects in index\n */\n readonly count: number;\n\n /**\n * @zh 获取所有对象\n * @en Get all objects\n */\n getAll(): T[];\n}\n\n// =============================================================================\n// 工具函数 | Utility Functions\n// =============================================================================\n\n/**\n * @zh 创建边界框\n * @en Create bounding box\n */\nexport function createBounds(minX: number, minY: number, maxX: number, maxY: number): IBounds {\n return { minX, minY, maxX, maxY };\n}\n\n/**\n * @zh 从中心点和尺寸创建边界框\n * @en Create bounding box from center and size\n */\nexport function createBoundsFromCenter(center: IVector2, width: number, height: number): IBounds {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n return {\n minX: center.x - halfWidth,\n minY: center.y - halfHeight,\n maxX: center.x + halfWidth,\n maxY: center.y + halfHeight\n };\n}\n\n/**\n * @zh 从圆形创建边界框\n * @en Create bounding box from circle\n */\nexport function createBoundsFromCircle(center: IVector2, radius: number): IBounds {\n return {\n minX: center.x - radius,\n minY: center.y - radius,\n maxX: center.x + radius,\n maxY: center.y + radius\n };\n}\n\n/**\n * @zh 检查点是否在边界框内\n * @en Check if point is inside bounds\n */\nexport function isPointInBounds(point: IVector2, bounds: IBounds): boolean {\n return point.x >= bounds.minX && point.x <= bounds.maxX &&\n point.y >= bounds.minY && point.y <= bounds.maxY;\n}\n\n/**\n * @zh 检查两个边界框是否相交\n * @en Check if two bounding boxes intersect\n */\nexport function boundsIntersect(a: IBounds, b: IBounds): boolean {\n return a.minX <= b.maxX && a.maxX >= b.minX &&\n a.minY <= b.maxY && a.maxY >= b.minY;\n}\n\n/**\n * @zh 检查边界框是否与圆形相交\n * @en Check if bounds intersects with circle\n */\nexport function boundsIntersectsCircle(bounds: IBounds, center: IVector2, radius: number): boolean {\n const closestX = Math.max(bounds.minX, Math.min(center.x, bounds.maxX));\n const closestY = Math.max(bounds.minY, Math.min(center.y, bounds.maxY));\n const distanceX = center.x - closestX;\n const distanceY = center.y - closestY;\n return (distanceX * distanceX + distanceY * distanceY) <= (radius * radius);\n}\n\n/**\n * @zh 计算两点之间的距离平方\n * @en Calculate squared distance between two points\n */\nexport function distanceSquared(a: IVector2, b: IVector2): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * @zh 计算两点之间的距离\n * @en Calculate distance between two points\n */\nexport function distance(a: IVector2, b: IVector2): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n","/**\n * @zh 网格空间索引\n * @en Grid Spatial Index\n *\n * @zh 基于均匀网格的空间索引实现\n * @en Uniform grid based spatial index implementation\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\nimport type {\n ISpatialIndex,\n IBounds,\n IRaycastHit,\n SpatialFilter\n} from './ISpatialQuery';\nimport {\n createBoundsFromCircle,\n boundsIntersectsCircle,\n distanceSquared,\n distance\n} from './ISpatialQuery';\n\n// =============================================================================\n// 网格项 | Grid Item\n// =============================================================================\n\n/**\n * @zh 网格中的项\n * @en Item in grid\n */\ninterface GridItem<T> {\n item: T;\n position: IVector2;\n cellKey: string;\n}\n\n// =============================================================================\n// 网格空间索引 | Grid Spatial Index\n// =============================================================================\n\n/**\n * @zh 网格空间索引配置\n * @en Grid spatial index configuration\n */\nexport interface GridSpatialIndexConfig {\n /**\n * @zh 网格单元格大小\n * @en Grid cell size\n */\n cellSize: number;\n}\n\n/**\n * @zh 网格空间索引实现\n * @en Grid spatial index implementation\n *\n * @zh 使用均匀网格进行空间划分,适合对象分布均匀的场景\n * @en Uses uniform grid for spatial partitioning, suitable for evenly distributed objects\n */\nexport class GridSpatialIndex<T> implements ISpatialIndex<T> {\n private readonly _cellSize: number;\n private readonly _cells: Map<string, Set<GridItem<T>>> = new Map();\n private readonly _itemMap: Map<T, GridItem<T>> = new Map();\n\n constructor(config: GridSpatialIndexConfig) {\n this._cellSize = config.cellSize;\n }\n\n // =========================================================================\n // ISpatialIndex 实现 | ISpatialIndex Implementation\n // =========================================================================\n\n get count(): number {\n return this._itemMap.size;\n }\n\n /**\n * @zh 插入对象\n * @en Insert object\n */\n insert(item: T, position: IVector2): void {\n if (this._itemMap.has(item)) {\n this.update(item, position);\n return;\n }\n\n const cellKey = this._getCellKey(position);\n const gridItem: GridItem<T> = { item, position: { x: position.x, y: position.y }, cellKey };\n\n this._itemMap.set(item, gridItem);\n this._addToCell(cellKey, gridItem);\n }\n\n /**\n * @zh 移除对象\n * @en Remove object\n */\n remove(item: T): boolean {\n const gridItem = this._itemMap.get(item);\n if (!gridItem) {\n return false;\n }\n\n this._removeFromCell(gridItem.cellKey, gridItem);\n this._itemMap.delete(item);\n return true;\n }\n\n /**\n * @zh 更新对象位置\n * @en Update object position\n */\n update(item: T, newPosition: IVector2): boolean {\n const gridItem = this._itemMap.get(item);\n if (!gridItem) {\n return false;\n }\n\n const newCellKey = this._getCellKey(newPosition);\n\n if (newCellKey !== gridItem.cellKey) {\n this._removeFromCell(gridItem.cellKey, gridItem);\n gridItem.cellKey = newCellKey;\n this._addToCell(newCellKey, gridItem);\n }\n\n gridItem.position = { x: newPosition.x, y: newPosition.y };\n return true;\n }\n\n /**\n * @zh 清空索引\n * @en Clear index\n */\n clear(): void {\n this._cells.clear();\n this._itemMap.clear();\n }\n\n /**\n * @zh 获取所有对象\n * @en Get all objects\n */\n getAll(): T[] {\n return Array.from(this._itemMap.keys());\n }\n\n // =========================================================================\n // ISpatialQuery 实现 | ISpatialQuery Implementation\n // =========================================================================\n\n /**\n * @zh 查找半径内的所有对象\n * @en Find all objects within radius\n */\n findInRadius(center: IVector2, radius: number, filter?: SpatialFilter<T>): T[] {\n const results: T[] = [];\n const radiusSq = radius * radius;\n const bounds = createBoundsFromCircle(center, radius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq <= radiusSq) {\n if (!filter || filter(gridItem.item)) {\n results.push(gridItem.item);\n }\n }\n });\n\n return results;\n }\n\n /**\n * @zh 查找矩形区域内的所有对象\n * @en Find all objects within rectangle\n */\n findInRect(bounds: IBounds, filter?: SpatialFilter<T>): T[] {\n const results: T[] = [];\n\n this._forEachInBounds(bounds, (gridItem) => {\n const pos = gridItem.position;\n if (pos.x >= bounds.minX && pos.x <= bounds.maxX &&\n pos.y >= bounds.minY && pos.y <= bounds.maxY) {\n if (!filter || filter(gridItem.item)) {\n results.push(gridItem.item);\n }\n }\n });\n\n return results;\n }\n\n /**\n * @zh 查找最近的对象\n * @en Find nearest object\n */\n findNearest(center: IVector2, maxDistance?: number, filter?: SpatialFilter<T>): T | null {\n let nearest: T | null = null;\n let nearestDistSq = maxDistance !== undefined ? maxDistance * maxDistance : Infinity;\n\n const searchRadius = maxDistance ?? this._cellSize * 10;\n const bounds = createBoundsFromCircle(center, searchRadius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq < nearestDistSq) {\n if (!filter || filter(gridItem.item)) {\n nearest = gridItem.item;\n nearestDistSq = distSq;\n }\n }\n });\n\n return nearest;\n }\n\n /**\n * @zh 查找最近的 K 个对象\n * @en Find K nearest objects\n */\n findKNearest(center: IVector2, k: number, maxDistance?: number, filter?: SpatialFilter<T>): T[] {\n if (k <= 0) return [];\n\n const candidates: Array<{ item: T; distSq: number }> = [];\n const maxDistSq = maxDistance !== undefined ? maxDistance * maxDistance : Infinity;\n const searchRadius = maxDistance ?? this._cellSize * 10;\n const bounds = createBoundsFromCircle(center, searchRadius);\n\n this._forEachInBounds(bounds, (gridItem) => {\n const distSq = distanceSquared(center, gridItem.position);\n if (distSq <= maxDistSq) {\n if (!filter || filter(gridItem.item)) {\n candidates.push({ item: gridItem.item, distSq });\n }\n }\n });\n\n candidates.sort((a, b) => a.distSq - b.distSq);\n return candidates.slice(0, k).map(c => c.item);\n }\n\n /**\n * @zh 射线检测\n * @en Raycast\n */\n raycast(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T>[] {\n const hits: IRaycastHit<T>[] = [];\n const rayBounds = this._getRayBounds(origin, direction, maxDistance);\n\n this._forEachInBounds(rayBounds, (gridItem) => {\n const hit = this._rayIntersectsPoint(origin, direction, gridItem.position, maxDistance);\n if (hit && hit.distance <= maxDistance) {\n if (!filter || filter(gridItem.item)) {\n hits.push({\n target: gridItem.item,\n point: hit.point,\n normal: hit.normal,\n distance: hit.distance\n });\n }\n }\n });\n\n hits.sort((a, b) => a.distance - b.distance);\n return hits;\n }\n\n /**\n * @zh 射线检测(仅返回第一个命中)\n * @en Raycast (return first hit only)\n */\n raycastFirst(origin: IVector2, direction: IVector2, maxDistance: number, filter?: SpatialFilter<T>): IRaycastHit<T> | null {\n const hits = this.raycast(origin, direction, maxDistance, filter);\n return hits.length > 0 ? hits[0] : null;\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n private _getCellKey(position: IVector2): string {\n const cellX = Math.floor(position.x / this._cellSize);\n const cellY = Math.floor(position.y / this._cellSize);\n return `${cellX},${cellY}`;\n }\n\n private _getCellCoords(position: IVector2): { x: number; y: number } {\n return {\n x: Math.floor(position.x / this._cellSize),\n y: Math.floor(position.y / this._cellSize)\n };\n }\n\n private _addToCell(cellKey: string, gridItem: GridItem<T>): void {\n let cell = this._cells.get(cellKey);\n if (!cell) {\n cell = new Set();\n this._cells.set(cellKey, cell);\n }\n cell.add(gridItem);\n }\n\n private _removeFromCell(cellKey: string, gridItem: GridItem<T>): void {\n const cell = this._cells.get(cellKey);\n if (cell) {\n cell.delete(gridItem);\n if (cell.size === 0) {\n this._cells.delete(cellKey);\n }\n }\n }\n\n private _forEachInBounds(bounds: IBounds, callback: (item: GridItem<T>) => void): void {\n const minCell = this._getCellCoords({ x: bounds.minX, y: bounds.minY });\n const maxCell = this._getCellCoords({ x: bounds.maxX, y: bounds.maxY });\n\n for (let x = minCell.x; x <= maxCell.x; x++) {\n for (let y = minCell.y; y <= maxCell.y; y++) {\n const cell = this._cells.get(`${x},${y}`);\n if (cell) {\n for (const gridItem of cell) {\n callback(gridItem);\n }\n }\n }\n }\n }\n\n private _getRayBounds(origin: IVector2, direction: IVector2, maxDistance: number): IBounds {\n const endX = origin.x + direction.x * maxDistance;\n const endY = origin.y + direction.y * maxDistance;\n\n return {\n minX: Math.min(origin.x, endX),\n minY: Math.min(origin.y, endY),\n maxX: Math.max(origin.x, endX),\n maxY: Math.max(origin.y, endY)\n };\n }\n\n private _rayIntersectsPoint(\n origin: IVector2,\n direction: IVector2,\n point: IVector2,\n _maxDistance: number,\n hitRadius: number = 0.5\n ): { point: IVector2; normal: IVector2; distance: number } | null {\n const toPoint = { x: point.x - origin.x, y: point.y - origin.y };\n const projection = toPoint.x * direction.x + toPoint.y * direction.y;\n\n if (projection < 0) {\n return null;\n }\n\n const closestX = origin.x + direction.x * projection;\n const closestY = origin.y + direction.y * projection;\n const distToLine = Math.sqrt(\n (point.x - closestX) * (point.x - closestX) +\n (point.y - closestY) * (point.y - closestY)\n );\n\n if (distToLine <= hitRadius) {\n const hitDist = projection - Math.sqrt(hitRadius * hitRadius - distToLine * distToLine);\n if (hitDist >= 0) {\n const hitPoint = {\n x: origin.x + direction.x * hitDist,\n y: origin.y + direction.y * hitDist\n };\n const normal = {\n x: hitPoint.x - point.x,\n y: hitPoint.y - point.y\n };\n const normalLen = Math.sqrt(normal.x * normal.x + normal.y * normal.y);\n if (normalLen > 0) {\n normal.x /= normalLen;\n normal.y /= normalLen;\n }\n return { point: hitPoint, normal, distance: hitDist };\n }\n }\n\n return null;\n }\n}\n\n// =============================================================================\n// 工厂函数 | Factory Functions\n// =============================================================================\n\n/**\n * @zh 创建网格空间索引\n * @en Create grid spatial index\n */\nexport function createGridSpatialIndex<T>(cellSize: number = 100): GridSpatialIndex<T> {\n return new GridSpatialIndex<T>({ cellSize });\n}\n","/**\n * @zh 网格 AOI 实现\n * @en Grid AOI Implementation\n *\n * @zh 基于均匀网格的兴趣区域管理实现\n * @en Uniform grid based area of interest management implementation\n */\n\nimport type { IVector2 } from '@esengine/ecs-framework-math';\nimport type {\n IAOIManager,\n IAOIObserverConfig,\n IAOIEvent,\n AOIEventListener\n} from './IAOI';\nimport { distanceSquared } from '../ISpatialQuery';\n\n// =============================================================================\n// 内部类型 | Internal Types\n// =============================================================================\n\n/**\n * @zh AOI 观察者数据\n * @en AOI observer data\n */\ninterface AOIObserverData<T> {\n entity: T;\n position: IVector2;\n viewRange: number;\n viewRangeSq: number;\n observable: boolean;\n cellKey: string;\n /** @zh 当前可见的实体集合 @en Currently visible entities */\n visibleEntities: Set<T>;\n /** @zh 实体特定的监听器 @en Entity-specific listeners */\n listeners: Set<AOIEventListener<T>>;\n}\n\n// =============================================================================\n// 网格 AOI 配置 | Grid AOI Configuration\n// =============================================================================\n\n/**\n * @zh 网格 AOI 配置\n * @en Grid AOI configuration\n */\nexport interface GridAOIConfig {\n /**\n * @zh 网格单元格大小(建议设置为平均视野范围的 1-2 倍)\n * @en Grid cell size (recommended 1-2x average view range)\n */\n cellSize: number;\n}\n\n// =============================================================================\n// 网格 AOI 实现 | Grid AOI Implementation\n// =============================================================================\n\n/**\n * @zh 网格 AOI 实现\n * @en Grid AOI implementation\n *\n * @zh 使用均匀网格进行空间划分,高效管理大量实体的兴趣区域\n * @en Uses uniform grid for spatial partitioning, efficiently managing AOI for many entities\n */\nexport class GridAOI<T> implements IAOIManager<T> {\n private readonly _cellSize: number;\n private readonly _cells: Map<string, Set<AOIObserverData<T>>> = new Map();\n private readonly _observers: Map<T, AOIObserverData<T>> = new Map();\n private readonly _globalListeners: Set<AOIEventListener<T>> = new Set();\n\n constructor(config: GridAOIConfig) {\n this._cellSize = config.cellSize;\n }\n\n // =========================================================================\n // IAOIManager 实现 | IAOIManager Implementation\n // =========================================================================\n\n get count(): number {\n return this._observers.size;\n }\n\n /**\n * @zh 添加观察者\n * @en Add observer\n */\n addObserver(entity: T, position: IVector2, config: IAOIObserverConfig): void {\n if (this._observers.has(entity)) {\n this.updatePosition(entity, position);\n this.updateViewRange(entity, config.viewRange);\n return;\n }\n\n const cellKey = this._getCellKey(position);\n const data: AOIObserverData<T> = {\n entity,\n position: { x: position.x, y: position.y },\n viewRange: config.viewRange,\n viewRangeSq: config.viewRange * config.viewRange,\n observable: config.observable !== false,\n cellKey,\n visibleEntities: new Set(),\n listeners: new Set()\n };\n\n this._observers.set(entity, data);\n this._addToCell(cellKey, data);\n\n // Initial visibility check for this observer\n this._updateVisibility(data);\n\n // Notify other observers about this new entity\n if (data.observable) {\n this._updateObserversOfEntity(data);\n }\n }\n\n /**\n * @zh 移除观察者\n * @en Remove observer\n */\n removeObserver(entity: T): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n // Notify all observers who were watching this entity\n if (data.observable) {\n for (const [, otherData] of this._observers) {\n if (otherData !== data && otherData.visibleEntities.has(entity)) {\n otherData.visibleEntities.delete(entity);\n this._emitEvent({\n type: 'exit',\n observer: otherData.entity,\n target: entity,\n position: data.position\n }, otherData);\n }\n }\n }\n\n // Notify this entity about all entities it was watching\n for (const visible of data.visibleEntities) {\n const visibleData = this._observers.get(visible);\n if (visibleData) {\n this._emitEvent({\n type: 'exit',\n observer: entity,\n target: visible,\n position: visibleData.position\n }, data);\n }\n }\n\n this._removeFromCell(data.cellKey, data);\n this._observers.delete(entity);\n return true;\n }\n\n /**\n * @zh 更新观察者位置\n * @en Update observer position\n */\n updatePosition(entity: T, newPosition: IVector2): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n const newCellKey = this._getCellKey(newPosition);\n\n // Update cell if changed\n if (newCellKey !== data.cellKey) {\n this._removeFromCell(data.cellKey, data);\n data.cellKey = newCellKey;\n this._addToCell(newCellKey, data);\n }\n\n data.position = { x: newPosition.x, y: newPosition.y };\n\n // Update visibility for this observer\n this._updateVisibility(data);\n\n // Update visibility for others who might now see/unsee this entity\n if (data.observable) {\n this._updateObserversOfEntity(data);\n }\n\n return true;\n }\n\n /**\n * @zh 更新观察者视野范围\n * @en Update observer view range\n */\n updateViewRange(entity: T, newRange: number): boolean {\n const data = this._observers.get(entity);\n if (!data) {\n return false;\n }\n\n data.viewRange = newRange;\n data.viewRangeSq = newRange * newRange;\n\n // Recalculate visibility\n this._updateVisibility(data);\n\n return true;\n }\n\n /**\n * @zh 获取实体视野内的所有对象\n * @en Get all objects within entity's view\n */\n getEntitiesInView(entity: T): T[] {\n const data = this._observers.get(entity);\n if (!data) {\n return [];\n }\n return Array.from(data.visibleEntities);\n }\n\n /**\n * @zh 获取能看到指定实体的所有观察者\n * @en Get all observers who can see the specified entity\n */\n getObserversOf(entity: T): T[] {\n const data = this._observers.get(entity);\n if (!data || !data.observable) {\n return [];\n }\n\n const observers: T[] = [];\n for (const [, otherData] of this._observers) {\n if (otherData !== data && otherData.visibleEntities.has(entity)) {\n observers.push(otherData.entity);\n }\n }\n return observers;\n }\n\n /**\n * @zh 检查观察者是否能看到目标\n * @en Check if observer can see target\n */\n canSee(observer: T, target: T): boolean {\n const data = this._observers.get(observer);\n if (!data) {\n return false;\n }\n return data.visibleEntities.has(target);\n }\n\n /**\n * @zh 添加全局事件监听器\n * @en Add global event listener\n */\n addListener(listener: AOIEventListener<T>): void {\n this._globalListeners.add(listener);\n }\n\n /**\n * @zh 移除全局事件监听器\n * @en Remove global event listener\n */\n removeListener(listener: AOIEventListener<T>): void {\n this._globalListeners.delete(listener);\n }\n\n /**\n * @zh 为特定观察者添加事件监听器\n * @en Add event listener for specific observer\n */\n addEntityListener(entity: T, listener: AOIEventListener<T>): void {\n const data = this._observers.get(entity);\n if (data) {\n data.listeners.add(listener);\n }\n }\n\n /**\n * @zh 移除特定观察者的事件监听器\n * @en Remove event listener for specific observer\n */\n removeEntityListener(entity: T, listener: AOIEventListener<T>): void {\n const data = this._observers.get(entity);\n if (data) {\n data.listeners.delete(listener);\n }\n }\n\n /**\n * @zh 清空所有观察者\n * @en Clear all observers\n */\n clear(): void {\n this._cells.clear();\n this._observers.clear();\n }\n\n // =========================================================================\n // 私有方法 | Private Methods\n // =========================================================================\n\n private _getCellKey(position: IVector2): string {\n const cellX = Math.floor(position.x / this._cellSize);\n const cellY = Math.floor(position.y / this._cellSize);\n return `${cellX},${cellY}`;\n }\n\n private _getCellCoords(position: IVector2): { x: number; y: number } {\n return {\n x: Math.floor(position.x / this._cellSize),\n y: Math.floor(position.y / this._cellSize)\n };\n }\n\n private _addToCell(cellKey: string, data: AOIObserverData<T>): void {\n let cell = this._cells.get(cellKey);\n if (!cell) {\n cell = new Set();\n this._cells.set(cellKey, cell);\n }\n cell.add(data);\n }\n\n private _removeFromCell(cellKey: string, data: AOIObserverData<T>): void {\n const cell = this._cells.get(cellKey);\n if (cell) {\n cell.delete(data);\n if (cell.size === 0) {\n this._cells.delete(cellKey);\n }\n }\n }\n\n /**\n * @zh 更新观察者的可见实体列表\n * @en Update observer's visible entities list\n */\n private _updateVisibility(data: AOIObserverData<T>): void {\n const newVisible = new Set<T>();\n\n // Calculate search radius in cells\n const cellRadius = Math.ceil(data.viewRange / this._cellSize);\n const centerCell = this._getCellCoords(data.position);\n\n // Check all cells within range\n for (let dx = -cellRadius; dx <= cellRadius; dx++) {\n for (let dy = -cellRadius; dy <= cellRadius; dy++) {\n const cellKey = `${centerCell.x + dx},${centerCell.y + dy}`;\n const cell = this._cells.get(cellKey);\n if (!cell) continue;\n\n for (const otherData of cell) {\n if (otherData === data) continue;\n if (!otherData.observable) continue;\n\n const distSq = distanceSquared(data.position, otherData.position);\n if (distSq <= data.viewRangeSq) {\n newVisible.add(otherData.entity);\n }\n }\n }\n }\n\n // Find entities that entered view\n for (const entity of newVisible) {\n if (!data.visibleEntities.has(entity)) {\n const targetData = this._observers.get(entity);\n if (targetData) {\n this._emitEvent({\n type: 'enter',\n observer: data.entity,\n target: entity,\n position: targetData.position\n }, data);\n }\n }\n }\n\n // Find entities that exited view\n for (const entity of data.visibleEntities) {\n if (!newVisible.has(entity)) {\n const targetData = this._observers.get(entity);\n const position = targetData?.position ?? { x: 0, y: 0 };\n this._emitEvent({\n type: 'exit',\n observer: data.entity,\n target: entity,\n position\n }, data);\n }\n }\n\n data.visibleEntities = newVisible;\n }\n\n /**\n * @zh 更新其他观察者对于某个实体的可见性\n * @en Update other observers' visibility of an entity\n */\n private _updateObserversOfEntity(movedData: AOIObserverData<T>): void {\n // Check all observers for visibility changes\n // This handles both: observers who can now see the entity (enter)\n // and observers who could see it before but can't anymore (exit)\n for (const [, otherData] of this._observers) {\n if (otherData === movedData) continue;\n\n const distSq = distanceSquared(otherData.position, movedData.position);\n const wasVisible = otherData.visibleEntities.has(movedData.entity);\n const isVisible = distSq <= otherData.viewRangeSq;\n\n if (isVisible && !wasVisible) {\n otherData.visibleEntities.add(movedData.entity);\n this._emitEvent({\n type: 'enter',\n observer: otherData.entity,\n target: movedData.entity,\n position: movedData.position\n }, otherData);\n } else if (!isVisible && wasVisible) {\n otherData.visibleEntities.delete(movedData.entity);\n this._emitEvent({\n type: 'exit',\n observer: otherData.entity,\n target: movedData.entity,\n position: movedData.position\n }, otherData);\n }\n }\n }\n\n /**\n * @zh 获取最大视野范围(用于优化搜索)\n * @en Get maximum view range (for search optimization)\n */\n private _getMaxViewRange(): number {\n let max = 0;\n for (const [, data] of this._observers) {\n if (data.viewRange > max) {\n max = data.viewRange;\n }\n }\n return max;\n }\n\n /**\n * @zh 发送事件\n * @en Emit event\n */\n private _emitEvent(event: IAOIEvent<T>, observerData: AOIObserverData<T>): void {\n // Entity-specific listeners\n for (const listener of observerData.listeners) {\n try {\n listener(event);\n } catch (e) {\n console.error('AOI entity listener error:', e);\n }\n }\n\n // Global listeners\n for (const listener of this._globalListeners) {\n try {\n listener(event);\n } catch (e) {\n console.error('AOI global listener error:', e);\n }\n }\n }\n}\n\n// =============================================================================\n// 工厂函数 | Factory Functions\n// =============================================================================\n\n/**\n * @zh 创建网格 AOI 管理器\n * @en Create grid AOI manager\n *\n * @param cellSize - @zh 网格单元格大小 @en Grid cell size\n */\nexport function createGridAOI<T>(cellSize: number = 100): GridAOI<T> {\n return new GridAOI<T>({ cellSize });\n}\n","/**\n * @zh 空间查询服务令牌\n * @en Spatial Query Service Tokens\n */\n\nimport { createServiceToken } from '@esengine/ecs-framework';\nimport type { ISpatialIndex, ISpatialQuery } from './ISpatialQuery';\nimport type { IAOIManager } from './aoi/IAOI';\n\n/**\n * @zh 空间索引服务令牌\n * @en Spatial index service token\n *\n * @zh 用于注入空间索引服务\n * @en Used for injecting spatial index service\n */\nexport const SpatialIndexToken = createServiceToken<ISpatialIndex<unknown>>('spatialIndex');\n\n/**\n * @zh 空间查询服务令牌\n * @en Spatial query service token\n *\n * @zh 用于注入空间查询服务(只读)\n * @en Used for injecting spatial query service (read-only)\n */\nexport const SpatialQueryToken = createServiceToken<ISpatialQuery<unknown>>('spatialQuery');\n\n/**\n * @zh AOI 管理器服务令牌\n * @en AOI manager service token\n *\n * @zh 用于注入 AOI 兴趣区域管理服务\n * @en Used for injecting AOI (Area of Interest) manager service\n */\nexport const AOIManagerToken = createServiceToken<IAOIManager<unknown>>('aoiManager');\n","/**\n * @zh 空间查询蓝图节点\n * @en Spatial Query Blueprint Nodes\n *\n * @zh 提供空间查询功能的蓝图节点\n * @en Provides blueprint nodes for spatial query functionality\n */\n\nimport type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';\nimport type { ISpatialQuery, IBounds } from '../ISpatialQuery';\n\n// =============================================================================\n// 执行上下文接口 | Execution Context Interface\n// =============================================================================\n\n/**\n * @zh 空间查询上下文\n * @en Spatial query context\n */\ninterface SpatialContext {\n spatialQuery: ISpatialQuery<unknown>;\n evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;\n setOutputs(nodeId: string, outputs: Record<string, unknown>): void;\n}\n\n// =============================================================================\n// FindInRadius 节点 | FindInRadius Node\n// =============================================================================\n\n/**\n * @zh FindInRadius 节点模板\n * @en FindInRadius node template\n */\nexport const FindInRadiusTemplate: BlueprintNodeTemplate = {\n type: 'FindInRadius',\n title: 'Find In Radius',\n category: 'entity',\n description: 'Find all objects within radius / 查找半径内的所有对象',\n keywords: ['spatial', 'find', 'radius', 'range', 'query'],\n menuPath: ['Spatial', 'Find In Radius'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'radius',\n displayName: 'Radius',\n type: 'float',\n defaultValue: 100\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindInRadius 节点执行器\n * @en FindInRadius node executor\n */\nexport class FindInRadiusExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const radius = ctx.evaluateInput(node.id, 'radius', 100) as number;\n\n const results = ctx.spatialQuery?.findInRadius({ x: centerX, y: centerY }, radius) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// FindInRect 节点 | FindInRect Node\n// =============================================================================\n\n/**\n * @zh FindInRect 节点模板\n * @en FindInRect node template\n */\nexport const FindInRectTemplate: BlueprintNodeTemplate = {\n type: 'FindInRect',\n title: 'Find In Rect',\n category: 'entity',\n description: 'Find all objects within rectangle / 查找矩形区域内的所有对象',\n keywords: ['spatial', 'find', 'rect', 'rectangle', 'area', 'query'],\n menuPath: ['Spatial', 'Find In Rect'],\n isPure: true,\n inputs: [\n {\n name: 'minX',\n displayName: 'Min X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'minY',\n displayName: 'Min Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxX',\n displayName: 'Max X',\n type: 'float',\n defaultValue: 100\n },\n {\n name: 'maxY',\n displayName: 'Max Y',\n type: 'float',\n defaultValue: 100\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindInRect 节点执行器\n * @en FindInRect node executor\n */\nexport class FindInRectExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const minX = ctx.evaluateInput(node.id, 'minX', 0) as number;\n const minY = ctx.evaluateInput(node.id, 'minY', 0) as number;\n const maxX = ctx.evaluateInput(node.id, 'maxX', 100) as number;\n const maxY = ctx.evaluateInput(node.id, 'maxY', 100) as number;\n\n const bounds: IBounds = { minX, minY, maxX, maxY };\n const results = ctx.spatialQuery?.findInRect(bounds) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// FindNearest 节点 | FindNearest Node\n// =============================================================================\n\n/**\n * @zh FindNearest 节点模板\n * @en FindNearest node template\n */\nexport const FindNearestTemplate: BlueprintNodeTemplate = {\n type: 'FindNearest',\n title: 'Find Nearest',\n category: 'entity',\n description: 'Find nearest object to a point / 查找距离点最近的对象',\n keywords: ['spatial', 'find', 'nearest', 'closest', 'query'],\n menuPath: ['Spatial', 'Find Nearest'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'result',\n displayName: 'Result',\n type: 'any'\n },\n {\n name: 'found',\n displayName: 'Found',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindNearest 节点执行器\n * @en FindNearest node executor\n */\nexport class FindNearestExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const result = ctx.spatialQuery?.findNearest({ x: centerX, y: centerY }, maxDistance) ?? null;\n\n return {\n outputs: {\n result,\n found: result !== null\n }\n };\n }\n}\n\n// =============================================================================\n// FindKNearest 节点 | FindKNearest Node\n// =============================================================================\n\n/**\n * @zh FindKNearest 节点模板\n * @en FindKNearest node template\n */\nexport const FindKNearestTemplate: BlueprintNodeTemplate = {\n type: 'FindKNearest',\n title: 'Find K Nearest',\n category: 'entity',\n description: 'Find K nearest objects to a point / 查找距离点最近的 K 个对象',\n keywords: ['spatial', 'find', 'nearest', 'k', 'query'],\n menuPath: ['Spatial', 'Find K Nearest'],\n isPure: true,\n inputs: [\n {\n name: 'centerX',\n displayName: 'Center X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'centerY',\n displayName: 'Center Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'k',\n displayName: 'K',\n type: 'int',\n defaultValue: 5\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'results',\n displayName: 'Results',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh FindKNearest 节点执行器\n * @en FindKNearest node executor\n */\nexport class FindKNearestExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const centerX = ctx.evaluateInput(node.id, 'centerX', 0) as number;\n const centerY = ctx.evaluateInput(node.id, 'centerY', 0) as number;\n const k = ctx.evaluateInput(node.id, 'k', 5) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const results = ctx.spatialQuery?.findKNearest({ x: centerX, y: centerY }, k, maxDistance) ?? [];\n\n return {\n outputs: {\n results,\n count: results.length\n }\n };\n }\n}\n\n// =============================================================================\n// Raycast 节点 | Raycast Node\n// =============================================================================\n\n/**\n * @zh Raycast 节点模板\n * @en Raycast node template\n */\nexport const RaycastTemplate: BlueprintNodeTemplate = {\n type: 'Raycast',\n title: 'Raycast',\n category: 'entity',\n description: 'Cast a ray and get all hits / 发射射线并获取所有命中',\n keywords: ['spatial', 'raycast', 'ray', 'hit', 'query'],\n menuPath: ['Spatial', 'Raycast'],\n isPure: true,\n inputs: [\n {\n name: 'originX',\n displayName: 'Origin X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'originY',\n displayName: 'Origin Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'directionX',\n displayName: 'Direction X',\n type: 'float',\n defaultValue: 1\n },\n {\n name: 'directionY',\n displayName: 'Direction Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'hits',\n displayName: 'Hits',\n type: 'array'\n },\n {\n name: 'hitCount',\n displayName: 'Hit Count',\n type: 'int'\n },\n {\n name: 'hasHit',\n displayName: 'Has Hit',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh Raycast 节点执行器\n * @en Raycast node executor\n */\nexport class RaycastExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;\n const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;\n const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;\n const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const hits = ctx.spatialQuery?.raycast(\n { x: originX, y: originY },\n { x: directionX, y: directionY },\n maxDistance\n ) ?? [];\n\n return {\n outputs: {\n hits,\n hitCount: hits.length,\n hasHit: hits.length > 0\n }\n };\n }\n}\n\n/**\n * @zh RaycastFirst 节点模板\n * @en RaycastFirst node template\n */\nexport const RaycastFirstTemplate: BlueprintNodeTemplate = {\n type: 'RaycastFirst',\n title: 'Raycast First',\n category: 'entity',\n description: 'Cast a ray and get first hit / 发射射线并获取第一个命中',\n keywords: ['spatial', 'raycast', 'ray', 'first', 'hit', 'query'],\n menuPath: ['Spatial', 'Raycast First'],\n isPure: true,\n inputs: [\n {\n name: 'originX',\n displayName: 'Origin X',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'originY',\n displayName: 'Origin Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'directionX',\n displayName: 'Direction X',\n type: 'float',\n defaultValue: 1\n },\n {\n name: 'directionY',\n displayName: 'Direction Y',\n type: 'float',\n defaultValue: 0\n },\n {\n name: 'maxDistance',\n displayName: 'Max Distance',\n type: 'float',\n defaultValue: 1000\n }\n ],\n outputs: [\n {\n name: 'hit',\n displayName: 'Hit',\n type: 'object'\n },\n {\n name: 'target',\n displayName: 'Target',\n type: 'any'\n },\n {\n name: 'hitPointX',\n displayName: 'Hit Point X',\n type: 'float'\n },\n {\n name: 'hitPointY',\n displayName: 'Hit Point Y',\n type: 'float'\n },\n {\n name: 'distance',\n displayName: 'Distance',\n type: 'float'\n },\n {\n name: 'hasHit',\n displayName: 'Has Hit',\n type: 'bool'\n }\n ],\n color: '#4a9eff'\n};\n\n/**\n * @zh RaycastFirst 节点执行器\n * @en RaycastFirst node executor\n */\nexport class RaycastFirstExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as SpatialContext;\n const originX = ctx.evaluateInput(node.id, 'originX', 0) as number;\n const originY = ctx.evaluateInput(node.id, 'originY', 0) as number;\n const directionX = ctx.evaluateInput(node.id, 'directionX', 1) as number;\n const directionY = ctx.evaluateInput(node.id, 'directionY', 0) as number;\n const maxDistance = ctx.evaluateInput(node.id, 'maxDistance', 1000) as number;\n\n const hit = ctx.spatialQuery?.raycastFirst(\n { x: originX, y: originY },\n { x: directionX, y: directionY },\n maxDistance\n ) ?? null;\n\n return {\n outputs: {\n hit,\n target: hit?.target ?? null,\n hitPointX: hit?.point.x ?? 0,\n hitPointY: hit?.point.y ?? 0,\n distance: hit?.distance ?? 0,\n hasHit: hit !== null\n }\n };\n }\n}\n\n// =============================================================================\n// 节点定义集合 | Node Definition Collection\n// =============================================================================\n\n/**\n * @zh 空间查询节点定义\n * @en Spatial query node definitions\n */\nexport const SpatialQueryNodeDefinitions = [\n { template: FindInRadiusTemplate, executor: new FindInRadiusExecutor() },\n { template: FindInRectTemplate, executor: new FindInRectExecutor() },\n { template: FindNearestTemplate, executor: new FindNearestExecutor() },\n { template: FindKNearestTemplate, executor: new FindKNearestExecutor() },\n { template: RaycastTemplate, executor: new RaycastExecutor() },\n { template: RaycastFirstTemplate, executor: new RaycastFirstExecutor() }\n];\n","/**\n * @zh AOI 蓝图节点\n * @en AOI Blueprint Nodes\n *\n * @zh 提供 AOI 功能的蓝图节点\n * @en Provides blueprint nodes for AOI functionality\n */\n\nimport type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';\nimport type { IAOIManager } from './IAOI';\n\n// =============================================================================\n// 执行上下文接口 | Execution Context Interface\n// =============================================================================\n\n/**\n * @zh AOI 上下文\n * @en AOI context\n */\ninterface AOIContext {\n aoiManager: IAOIManager<unknown>;\n entity: unknown;\n evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;\n setOutputs(nodeId: string, outputs: Record<string, unknown>): void;\n}\n\n// =============================================================================\n// GetEntitiesInView 节点 | GetEntitiesInView Node\n// =============================================================================\n\n/**\n * @zh GetEntitiesInView 节点模板\n * @en GetEntitiesInView node template\n */\nexport const GetEntitiesInViewTemplate: BlueprintNodeTemplate = {\n type: 'GetEntitiesInView',\n title: 'Get Entities In View',\n category: 'entity',\n description: 'Get all entities within view range / 获取视野范围内的所有实体',\n keywords: ['aoi', 'view', 'entities', 'visible'],\n menuPath: ['AOI', 'Get Entities In View'],\n isPure: true,\n inputs: [\n {\n name: 'observer',\n displayName: 'Observer',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'entities',\n displayName: 'Entities',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh GetEntitiesInView 节点执行器\n * @en GetEntitiesInView node executor\n */\nexport class GetEntitiesInViewExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const observer = ctx.evaluateInput(node.id, 'observer', ctx.entity);\n\n const entities = ctx.aoiManager?.getEntitiesInView(observer) ?? [];\n\n return {\n outputs: {\n entities,\n count: entities.length\n }\n };\n }\n}\n\n// =============================================================================\n// GetObserversOf 节点 | GetObserversOf Node\n// =============================================================================\n\n/**\n * @zh GetObserversOf 节点模板\n * @en GetObserversOf node template\n */\nexport const GetObserversOfTemplate: BlueprintNodeTemplate = {\n type: 'GetObserversOf',\n title: 'Get Observers Of',\n category: 'entity',\n description: 'Get all observers who can see the entity / 获取能看到该实体的所有观察者',\n keywords: ['aoi', 'observers', 'watchers', 'visible'],\n menuPath: ['AOI', 'Get Observers Of'],\n isPure: true,\n inputs: [\n {\n name: 'target',\n displayName: 'Target',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'observers',\n displayName: 'Observers',\n type: 'array'\n },\n {\n name: 'count',\n displayName: 'Count',\n type: 'int'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh GetObserversOf 节点执行器\n * @en GetObserversOf node executor\n */\nexport class GetObserversOfExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const target = ctx.evaluateInput(node.id, 'target', ctx.entity);\n\n const observers = ctx.aoiManager?.getObserversOf(target) ?? [];\n\n return {\n outputs: {\n observers,\n count: observers.length\n }\n };\n }\n}\n\n// =============================================================================\n// CanSee 节点 | CanSee Node\n// =============================================================================\n\n/**\n * @zh CanSee 节点模板\n * @en CanSee node template\n */\nexport const CanSeeTemplate: BlueprintNodeTemplate = {\n type: 'CanSee',\n title: 'Can See',\n category: 'entity',\n description: 'Check if observer can see target / 检查观察者是否能看到目标',\n keywords: ['aoi', 'visibility', 'can', 'see', 'check'],\n menuPath: ['AOI', 'Can See'],\n isPure: true,\n inputs: [\n {\n name: 'observer',\n displayName: 'Observer',\n type: 'object'\n },\n {\n name: 'target',\n displayName: 'Target',\n type: 'object'\n }\n ],\n outputs: [\n {\n name: 'canSee',\n displayName: 'Can See',\n type: 'bool'\n }\n ],\n color: '#9c27b0'\n};\n\n/**\n * @zh CanSee 节点执行器\n * @en CanSee node executor\n */\nexport class CanSeeExecutor implements INodeExecutor {\n execute(node: BlueprintNode, context: unknown): ExecutionResult {\n const ctx = context as AOIContext;\n const observer = ctx.evaluateInput(node.id, 'observer', ctx.entity);\n const target = ctx.evaluateInput(node.id, 'target', null);\n\n const canSee = ctx.aoiManager?.canSee(observer, target) ?? false;\n\n return {\n outputs: {\n canSee\n }\n };\n }\n}\n\n// =============================================================================\n// OnEntityEnterView 事件节点 | OnEntityEnterView Event Node\n// =============================================================================\n\n/**\n * @zh OnEntityEnterView 事件节点模板\n * @en OnEntityEnterView event node template\n */\nexport const OnEntityEnterViewTemplate: BlueprintNodeTemplate = {\n type: 'EventEntityEnterView',\n title: 'On Entity Enter View',\n category: 'event',\n description: 'Triggered when an entity enters view / 当实体进入视野时触发',\n keywords: ['aoi', 'event', 'enter', 'view', 'visible'],\n menuPath: ['AOI', 'Events', 'On Entity Enter View'],\n color: '#e91e63',\n inputs: [],\n outputs: [\n {\n name: 'exec',\n displayName: '',\n type: 'exec'\n },\n {\n name: 'entity',\n displayName: 'Entity',\n type: 'object'\n },\n {\n name: 'positionX',\n displayName: 'Position X',\n type: 'float'\n },\n {\n name: 'positionY',\n displayName: 'Position Y',\n type: 'float'\n }\n ]\n};\n\n/**\n * @zh OnEntityEnterView 事件执行器\n * @en OnEntityEnterView event executor\n */\nexport class OnEntityEnterViewExecutor implements INodeExecutor {\n execute(_node: BlueprintNode, _context: unknown): ExecutionResult {\n // Event nodes don't execute directly, they are triggered by the runtime\n return { nextExec: 'exec' };\n }\n}\n\n// =============================================================================\n// OnEntityExitView 事件节点 | OnEntityExitView Event Node\n// =============================================================================\n\n/**\n * @zh OnEntityExitView 事件节点模板\n * @en OnEntityExitView event node template\n */\nexport const OnEntityExitViewTemplate: BlueprintNodeTemplate = {\n type: 'EventEntityExitView',\n title: 'On Entity Exit View',\n category: 'event',\n description: 'Triggered when an entity exits view / 当实体离开视野时触发',\n keywords: ['aoi', 'event', 'exit', 'view', 'invisible'],\n menuPath: ['AOI', 'Events', 'On Entity Exit View'],\n color: '#e91e63',\n inputs: [],\n outputs: [\n {\n name: 'exec',\n displayName: '',\n type: 'exec'\n },\n {\n name: 'entity',\n displayName: 'Entity',\n type: 'object'\n },\n {\n name: 'positionX',\n displayName: 'Position X',\n type: 'float'\n },\n {\n name: 'positionY',\n displayName: 'Position Y',\n type: 'float'\n }\n ]\n};\n\n/**\n * @zh OnEntityExitView 事件执行器\n * @en OnEntityExitView event executor\n */\nexport class OnEntityExitViewExecutor implements INodeExecutor {\n execute(_node: BlueprintNode, _context: unknown): ExecutionResult {\n // Event nodes don't execute directly, they are triggered by the runtime\n return { nextExec: 'exec' };\n }\n}\n\n// =============================================================================\n// 节点定义集合 | Node Definition Collection\n// =============================================================================\n\n/**\n * @zh AOI 节点定义集合\n * @en AOI node definition collection\n */\nexport const AOINodeDefinitions = {\n templates: [\n GetEntitiesInViewTemplate,\n GetObserversOfTemplate,\n CanSeeTemplate,\n OnEntityEnterViewTemplate,\n OnEntityExitViewTemplate\n ],\n executors: new Map<string, INodeExecutor>([\n ['GetEntitiesInView', new GetEntitiesInViewExecutor()],\n ['GetObserversOf', new GetObserversOfExecutor()],\n ['CanSee', new CanSeeExecutor()],\n ['EventEntityEnterView', new OnEntityEnterViewExecutor()],\n ['EventEntityExitView', new OnEntityExitViewExecutor()]\n ])\n};\n"],"mappings":"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","names":["createBounds","minX","minY","maxX","maxY","createBoundsFromCenter","center","width","height","halfWidth","halfHeight","x","y","createBoundsFromCircle","radius","isPointInBounds","point","bounds","boundsIntersect","a","b","boundsIntersectsCircle","closestX","Math","max","min","closestY","distanceX","distanceY","distanceSquared","dx","dy","distance","sqrt","GridSpatialIndex","config","_cellSize","_cells","Map","_itemMap","cellSize","count","size","insert","item","position","has","update","cellKey","_getCellKey","gridItem","x","y","set","_addToCell","remove","get","_removeFromCell","delete","newPosition","newCellKey","clear","getAll","Array","from","keys","findInRadius","center","radius","filter","results","radiusSq","bounds","createBoundsFromCircle","_forEachInBounds","distSq","distanceSquared","push","findInRect","pos","minX","maxX","minY","maxY","findNearest","maxDistance","nearest","nearestDistSq","undefined","Infinity","searchRadius","findKNearest","k","candidates","maxDistSq","sort","a","b","slice","map","c","raycast","origin","direction","hits","rayBounds","_getRayBounds","hit","_rayIntersectsPoint","distance","target","point","normal","raycastFirst","length","cellX","Math","floor","cellY","_getCellCoords","cell","Set","add","callback","minCell","maxCell","endX","endY","min","max","_maxDistance","hitRadius","toPoint","projection","closestX","closestY","distToLine","sqrt","hitDist","hitPoint","normalLen","createGridSpatialIndex","GridAOI","config","_cellSize","_cells","Map","_observers","_globalListeners","Set","cellSize","count","size","addObserver","entity","position","has","updatePosition","updateViewRange","viewRange","cellKey","_getCellKey","data","x","y","viewRangeSq","observable","visibleEntities","listeners","set","_addToCell","_updateVisibility","_updateObserversOfEntity","removeObserver","get","otherData","delete","_emitEvent","type","observer","target","visible","visibleData","_removeFromCell","newPosition","newCellKey","newRange","getEntitiesInView","Array","from","getObserversOf","observers","push","canSee","addListener","listener","add","removeListener","addEntityListener","removeEntityListener","clear","cellX","Math","floor","cellY","_getCellCoords","cell","newVisible","cellRadius","ceil","centerCell","dx","dy","distSq","distanceSquared","targetData","movedData","wasVisible","isVisible","_getMaxViewRange","max","event","observerData","e","console","error","createGridAOI","createServiceToken","SpatialIndexToken","SpatialQueryToken","AOIManagerToken","FindInRadiusTemplate","type","title","category","description","keywords","menuPath","isPure","inputs","name","displayName","defaultValue","outputs","color","FindInRadiusExecutor","execute","node","context","ctx","centerX","evaluateInput","id","centerY","radius","results","spatialQuery","findInRadius","x","y","count","length","FindInRectTemplate","FindInRectExecutor","minX","minY","maxX","maxY","bounds","findInRect","FindNearestTemplate","FindNearestExecutor","maxDistance","result","findNearest","found","FindKNearestTemplate","FindKNearestExecutor","k","findKNearest","RaycastTemplate","RaycastExecutor","originX","originY","directionX","directionY","hits","raycast","hitCount","hasHit","RaycastFirstTemplate","RaycastFirstExecutor","hit","raycastFirst","target","hitPointX","point","hitPointY","distance","SpatialQueryNodeDefinitions","template","executor","GetEntitiesInViewTemplate","type","title","category","description","keywords","menuPath","isPure","inputs","name","displayName","outputs","color","GetEntitiesInViewExecutor","execute","node","context","ctx","observer","evaluateInput","id","entity","entities","aoiManager","getEntitiesInView","count","length","GetObserversOfTemplate","GetObserversOfExecutor","target","observers","getObserversOf","CanSeeTemplate","CanSeeExecutor","canSee","OnEntityEnterViewTemplate","OnEntityEnterViewExecutor","_node","_context","nextExec","OnEntityExitViewTemplate","OnEntityExitViewExecutor","AOINodeDefinitions","templates","executors","Map"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esengine/spatial",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Spatial query and indexing system for ECS Framework / ECS 框架的空间查询和索引系统",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,12 +26,35 @@
26
26
  "typescript": "^5.8.3",
27
27
  "@esengine/ecs-framework": "2.4.2",
28
28
  "@esengine/ecs-framework-math": "1.0.5",
29
+ "@esengine/build-config": "1.0.0",
30
+ "@esengine/blueprint": "1.0.0"
31
+ },
32
+ "peerDependencies": {
33
+ "@esengine/ecs-framework": "2.4.2",
29
34
  "@esengine/blueprint": "1.0.0",
30
- "@esengine/build-config": "1.0.0"
35
+ "@esengine/ecs-framework-math": "1.0.5"
31
36
  },
32
37
  "publishConfig": {
33
- "access": "public"
38
+ "access": "public",
39
+ "registry": "https://registry.npmjs.org/"
34
40
  },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/esengine/esengine.git",
44
+ "directory": "packages/framework/spatial"
45
+ },
46
+ "keywords": [
47
+ "ecs",
48
+ "spatial",
49
+ "spatial-index",
50
+ "grid",
51
+ "aoi",
52
+ "area-of-interest",
53
+ "game-engine",
54
+ "cocos",
55
+ "laya",
56
+ "esengine"
57
+ ],
35
58
  "scripts": {
36
59
  "build": "tsup",
37
60
  "build:watch": "tsup --watch",