Feature/physics and tilemap enhancement (#247)

* feat(behavior-tree,tilemap): 修复编辑器连线缩放问题并增强插件系统

* feat(node-editor,blueprint): 新增通用节点编辑器和蓝图可视化脚本系统

* feat(editor,tilemap): 优化编辑器UI样式和Tilemap编辑器功能

* fix: 修复CodeQL安全警告和CI类型检查错误

* fix: 修复CodeQL安全警告和CI类型检查错误

* fix: 修复CodeQL安全警告和CI类型检查错误
This commit is contained in:
YHH
2025-11-29 23:00:48 +08:00
committed by GitHub
parent f03b73b58e
commit 359886c72f
198 changed files with 33879 additions and 13121 deletions

View File

@@ -0,0 +1,225 @@
import React, { useMemo } from 'react';
import { Connection } from '../../domain/models/Connection';
import { PinCategory } from '../../domain/value-objects/PinType';
import { Position } from '../../domain/value-objects/Position';
export interface ConnectionLineProps {
/** Connection data (连接数据) */
connection: Connection;
/** Start position (起点位置) */
from: Position;
/** End position (终点位置) */
to: Position;
/** Whether the connection is selected (连接是否被选中) */
isSelected?: boolean;
/** Whether to show flow animation for exec connections (是否显示执行连接的流动动画) */
animated?: boolean;
/** Click handler (点击处理) */
onClick?: (connectionId: string, e: React.MouseEvent) => void;
/** Context menu handler (右键菜单处理) */
onContextMenu?: (connectionId: string, e: React.MouseEvent) => void;
}
/**
* Calculates bezier curve control points for smooth connection
* 计算平滑连接的贝塞尔曲线控制点
*/
function calculateBezierPath(from: Position, to: Position): string {
const dx = to.x - from.x;
// Calculate control point offset based on distance
// 根据距离计算控制点偏移
const curvature = Math.min(Math.abs(dx) * 0.5, 150);
// Horizontal bezier curve (水平贝塞尔曲线)
const cp1x = from.x + curvature;
const cp1y = from.y;
const cp2x = to.x - curvature;
const cp2y = to.y;
return `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${to.x} ${to.y}`;
}
/**
* ConnectionLine - SVG bezier curve connection between pins
* ConnectionLine - 引脚之间的 SVG 贝塞尔曲线连接
*/
export const ConnectionLine: React.FC<ConnectionLineProps> = ({
connection,
from,
to,
isSelected = false,
animated = false,
onClick,
onContextMenu
}) => {
const pathD = useMemo(() => calculateBezierPath(from, to), [from, to]);
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
onClick?.(connection.id, e);
};
const handleContextMenu = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onContextMenu?.(connection.id, e);
};
const classNames = useMemo(() => {
const classes = ['ne-connection', connection.category];
if (isSelected) classes.push('selected');
if (animated && connection.isExec) classes.push('animated');
return classes.join(' ');
}, [connection.category, connection.isExec, isSelected, animated]);
return (
<g>
{/* Hit area for easier selection (更容易选择的点击区域) */}
<path
className="ne-connection-hit"
d={pathD}
onClick={handleClick}
onContextMenu={handleContextMenu}
/>
{/* Glow effect (发光效果) */}
<path
className={`ne-connection-glow ${connection.category}`}
d={pathD}
/>
{/* Main connection line (主连接线) */}
<path
className={classNames}
d={pathD}
onClick={handleClick}
onContextMenu={handleContextMenu}
/>
</g>
);
};
export interface ConnectionPreviewProps {
/** Start position (起点位置) */
from: Position;
/** End position (current mouse position) (终点位置,当前鼠标位置) */
to: Position;
/** Pin category for coloring (引脚类型用于着色) */
category: PinCategory;
/** Whether the target is valid (目标是否有效) */
isValid?: boolean;
}
/**
* ConnectionPreview - Preview line shown while dragging a connection
* ConnectionPreview - 拖拽连接时显示的预览线
*/
export const ConnectionPreview: React.FC<ConnectionPreviewProps> = ({
from,
to,
category,
isValid
}) => {
const pathD = useMemo(() => calculateBezierPath(from, to), [from, to]);
const classNames = useMemo(() => {
const classes = ['ne-connection-preview', category];
if (isValid === true) classes.push('valid');
if (isValid === false) classes.push('invalid');
return classes.join(' ');
}, [category, isValid]);
return (
<path
className={classNames}
d={pathD}
/>
);
};
export interface ConnectionLayerProps {
/** All connections to render (要渲染的所有连接) */
connections: Connection[];
/** Function to get pin position by ID (通过ID获取引脚位置的函数) */
getPinPosition: (pinId: string) => Position | undefined;
/** Currently selected connection IDs (当前选中的连接ID) */
selectedConnectionIds?: Set<string>;
/** Whether to animate exec connections (是否动画化执行连接) */
animateExec?: boolean;
/** Preview connection while dragging (拖拽时的预览连接) */
preview?: {
from: Position;
to: Position;
category: PinCategory;
isValid?: boolean;
};
/** Connection click handler (连接点击处理) */
onConnectionClick?: (connectionId: string, e: React.MouseEvent) => void;
/** Connection context menu handler (连接右键菜单处理) */
onConnectionContextMenu?: (connectionId: string, e: React.MouseEvent) => void;
}
/**
* ConnectionLayer - SVG layer containing all connection lines
* ConnectionLayer - 包含所有连接线的 SVG 层
*/
export const ConnectionLayer: React.FC<ConnectionLayerProps> = ({
connections,
getPinPosition,
selectedConnectionIds,
animateExec = false,
preview,
onConnectionClick,
onConnectionContextMenu
}) => {
return (
<svg className="ne-connection-layer">
{/* Render all connections (渲染所有连接) */}
{connections.map(connection => {
const from = getPinPosition(connection.fromPinId);
const to = getPinPosition(connection.toPinId);
if (!from || !to) return null;
return (
<ConnectionLine
key={connection.id}
connection={connection}
from={from}
to={to}
isSelected={selectedConnectionIds?.has(connection.id)}
animated={animateExec}
onClick={onConnectionClick}
onContextMenu={onConnectionContextMenu}
/>
);
})}
{/* Render preview connection (渲染预览连接) */}
{preview && (
<ConnectionPreview
from={preview.from}
to={preview.to}
category={preview.category}
isValid={preview.isValid}
/>
)}
</svg>
);
};
export default ConnectionLine;