feat(spatial): 空间查询和索引系统 (#324)

* feat(spatial): 添加空间查询包

- ISpatialQuery: 空间查询接口
  - findInRadius, findInRect, findNearest, findKNearest
  - raycast, raycastFirst
  - IBounds, IRaycastHit, SpatialFilter 类型

- ISpatialIndex: 空间索引接口
  - insert, remove, update, clear, getAll

- GridSpatialIndex: 网格空间索引实现
  - 基于均匀网格的空间划分
  - 支持所有 ISpatialQuery 操作

- 工具函数
  - createBounds, isPointInBounds, boundsIntersect
  - distanceSquared, distance

* feat(spatial): 添加空间查询蓝图节点

- 添加 FindInRadius/FindInRect/FindNearest/FindKNearest 节点
- 添加 Raycast/RaycastFirst 射线检测节点
- 每个节点包含模板和执行器
- 使用 menuPath: ['Spatial', ...] 组织节点菜单
This commit is contained in:
YHH
2025-12-25 12:15:06 +08:00
committed by GitHub
parent 4089051731
commit 068ca4bf69
13 changed files with 1696 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
/**
* TypeDoc Plugin for Bilingual Documentation
* TypeDoc 双语文档插件
*
* Supports @zh and @en tags for bilingual documentation generation.
* 支持 @zh 和 @en 标签来生成双语文档。
*
* @example
* ```typescript
* /**
* * @zh 组件基类,所有组件都应继承此类
* * @en Base class for all components
* *\/
* export class Component { }
* ```
*/
import { Application, Converter, ReflectionKind, Comment, CommentTag } from 'typedoc';
/**
* @zh TypeDoc 双语插件入口
* @en TypeDoc bilingual plugin entry
* @param {Application} app - TypeDoc 应用实例 | TypeDoc application instance
*/
export function load(app) {
// 注册自定义标签 | Register custom tags
app.options.addReader({
name: 'bilingual-tags',
order: 0,
supportsPackages: false,
read(container) {
// 添加 @zh 和 @en 作为已知标签
// Add @zh and @en as known tags
const knownTags = container.getValue('blockTags') || [];
if (!knownTags.includes('@zh')) {
knownTags.push('@zh');
}
if (!knownTags.includes('@en')) {
knownTags.push('@en');
}
container.setValue('blockTags', knownTags);
}
});
// 监听解析完成事件 | Listen for conversion completion
app.converter.on(Converter.EVENT_RESOLVE_BEGIN, (context) => {
processReflections(context.project);
});
}
/**
* @zh 处理所有反射对象的注释
* @en Process comments for all reflections
* @param {import('typedoc').ProjectReflection} project
*/
function processReflections(project) {
for (const reflection of project.getReflectionsByKind(ReflectionKind.All)) {
if (reflection.comment) {
processBilingualComment(reflection.comment);
}
// 处理签名注释 | Process signature comments
if ('signatures' in reflection && reflection.signatures) {
for (const sig of reflection.signatures) {
if (sig.comment) {
processBilingualComment(sig.comment);
}
}
}
}
}
/**
* @zh 处理单个注释的双语标签
* @en Process bilingual tags in a single comment
* @param {Comment} comment
*/
function processBilingualComment(comment) {
const zhTags = comment.blockTags?.filter(tag => tag.tag === '@zh') || [];
const enTags = comment.blockTags?.filter(tag => tag.tag === '@en') || [];
if (zhTags.length === 0 && enTags.length === 0) {
return;
}
// 构建双语摘要 | Build bilingual summary
const parts = [];
// 添加中文部分 | Add Chinese part
if (zhTags.length > 0) {
const zhText = zhTags.map(tag => tagContentToString(tag)).join('\n');
parts.push(zhText);
}
// 添加英文部分 | Add English part
if (enTags.length > 0) {
const enText = enTags.map(tag => tagContentToString(tag)).join('\n');
if (parts.length > 0) {
parts.push(''); // 空行分隔 | Empty line separator
}
parts.push(enText);
}
// 如果有双语内容,更新摘要 | Update summary if bilingual content exists
if (parts.length > 0) {
// 保留原有摘要(如果不是来自 @zh/@en| Keep original summary if not from @zh/@en
const originalSummary = comment.summary?.map(part => {
if (typeof part === 'string') return part;
if (part.kind === 'text') return part.text;
return '';
}).join('') || '';
// 如果原有摘要不是空的且不是重复的,保留它
// Keep original summary if not empty and not duplicate
const bilingualText = parts.join('\n');
if (originalSummary && !bilingualText.includes(originalSummary.trim())) {
comment.summary = [
{ kind: 'text', text: originalSummary + '\n\n' + bilingualText }
];
} else {
comment.summary = [
{ kind: 'text', text: bilingualText }
];
}
}
// 移除已处理的 @zh/@en 标签,避免重复显示
// Remove processed @zh/@en tags to avoid duplicate display
comment.blockTags = comment.blockTags?.filter(
tag => tag.tag !== '@zh' && tag.tag !== '@en'
);
}
/**
* @zh 将标签内容转换为字符串
* @en Convert tag content to string
* @param {CommentTag} tag
* @returns {string}
*/
function tagContentToString(tag) {
if (!tag.content) return '';
return tag.content.map(part => {
if (typeof part === 'string') return part;
if (part.kind === 'text') return part.text;
if (part.kind === 'code') return '`' + part.text + '`';
return '';
}).join('');
}
export default { load };