feat(node-editor, blueprint): add group box and math/logic nodes (#438)
* feat(node-editor, blueprint): add group box and math/logic nodes node-editor: - Add visual group box for organizing nodes - Dynamic bounds calculation based on node pin counts - Groups auto-resize to wrap contained nodes - Dragging group header moves all nodes together blueprint: - Add comprehensive math nodes (modulo, power, sqrt, trig, etc.) - Add logic nodes (comparison, boolean, select) docs: - Update nodes.md with new math and logic nodes - Add group feature documentation to editor-guide.md * chore: remove unused debug and test scripts Remove FBX animation debug scripts that are no longer needed: - analyze-fbx, debug-*, test-*, verify-*, check-*, compare-*, trace-*, simple-fbx-test Remove unused kill-dev-server.js from editor-app
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
* - ecs: ECS 操作(Entity, Component, Flow)
|
||||
* - variables: 变量读写
|
||||
* - math: 数学运算
|
||||
* - logic: 比较和逻辑运算
|
||||
* - time: 时间工具
|
||||
* - debug: 调试工具
|
||||
*
|
||||
@@ -15,6 +16,7 @@
|
||||
* - ecs: ECS operations (Entity, Component, Flow)
|
||||
* - variables: Variable get/set
|
||||
* - math: Math operations
|
||||
* - logic: Comparison and logical operations
|
||||
* - time: Time utilities
|
||||
* - debug: Debug utilities
|
||||
*/
|
||||
@@ -31,6 +33,9 @@ export * from './variables';
|
||||
// Math operations | 数学运算
|
||||
export * from './math';
|
||||
|
||||
// Logic operations | 逻辑运算
|
||||
export * from './logic';
|
||||
|
||||
// Time utilities | 时间工具
|
||||
export * from './time';
|
||||
|
||||
|
||||
435
packages/framework/blueprint/src/nodes/logic/ComparisonNodes.ts
Normal file
435
packages/framework/blueprint/src/nodes/logic/ComparisonNodes.ts
Normal file
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* @zh 比较运算节点
|
||||
* @en Comparison Operation Nodes
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
// ============================================================================
|
||||
// Equal Node (等于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const EqualTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Equal',
|
||||
title: 'Equal',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A equals B (如果 A 等于 B 则返回 true)',
|
||||
keywords: ['equal', '==', 'same', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'any', displayName: 'A' },
|
||||
{ name: 'b', type: 'any', displayName: 'B' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(EqualTemplate)
|
||||
export class EqualExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = context.evaluateInput(node.id, 'a', null);
|
||||
const b = context.evaluateInput(node.id, 'b', null);
|
||||
return { outputs: { result: a === b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Not Equal Node (不等于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const NotEqualTemplate: BlueprintNodeTemplate = {
|
||||
type: 'NotEqual',
|
||||
title: 'Not Equal',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A does not equal B (如果 A 不等于 B 则返回 true)',
|
||||
keywords: ['not', 'equal', '!=', 'different', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'any', displayName: 'A' },
|
||||
{ name: 'b', type: 'any', displayName: 'B' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(NotEqualTemplate)
|
||||
export class NotEqualExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = context.evaluateInput(node.id, 'a', null);
|
||||
const b = context.evaluateInput(node.id, 'b', null);
|
||||
return { outputs: { result: a !== b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Greater Than Node (大于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const GreaterThanTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GreaterThan',
|
||||
title: 'Greater Than',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A is greater than B (如果 A 大于 B 则返回 true)',
|
||||
keywords: ['greater', 'than', '>', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GreaterThanTemplate)
|
||||
export class GreaterThanExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a > b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Greater Than Or Equal Node (大于等于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const GreaterThanOrEqualTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GreaterThanOrEqual',
|
||||
title: 'Greater Or Equal',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A is greater than or equal to B (如果 A 大于等于 B 则返回 true)',
|
||||
keywords: ['greater', 'equal', '>=', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GreaterThanOrEqualTemplate)
|
||||
export class GreaterThanOrEqualExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a >= b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Less Than Node (小于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const LessThanTemplate: BlueprintNodeTemplate = {
|
||||
type: 'LessThan',
|
||||
title: 'Less Than',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A is less than B (如果 A 小于 B 则返回 true)',
|
||||
keywords: ['less', 'than', '<', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(LessThanTemplate)
|
||||
export class LessThanExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a < b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Less Than Or Equal Node (小于等于节点)
|
||||
// ============================================================================
|
||||
|
||||
export const LessThanOrEqualTemplate: BlueprintNodeTemplate = {
|
||||
type: 'LessThanOrEqual',
|
||||
title: 'Less Or Equal',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if A is less than or equal to B (如果 A 小于等于 B 则返回 true)',
|
||||
keywords: ['less', 'equal', '<=', 'compare', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(LessThanOrEqualTemplate)
|
||||
export class LessThanOrEqualExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a <= b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// And Node (逻辑与节点)
|
||||
// ============================================================================
|
||||
|
||||
export const AndTemplate: BlueprintNodeTemplate = {
|
||||
type: 'And',
|
||||
title: 'AND',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if both A and B are true (如果 A 和 B 都为 true 则返回 true)',
|
||||
keywords: ['and', '&&', 'both', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'bool', displayName: 'A', defaultValue: false },
|
||||
{ name: 'b', type: 'bool', displayName: 'B', defaultValue: false }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(AndTemplate)
|
||||
export class AndExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Boolean(context.evaluateInput(node.id, 'a', false));
|
||||
const b = Boolean(context.evaluateInput(node.id, 'b', false));
|
||||
return { outputs: { result: a && b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Or Node (逻辑或节点)
|
||||
// ============================================================================
|
||||
|
||||
export const OrTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Or',
|
||||
title: 'OR',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if either A or B is true (如果 A 或 B 为 true 则返回 true)',
|
||||
keywords: ['or', '||', 'either', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'bool', displayName: 'A', defaultValue: false },
|
||||
{ name: 'b', type: 'bool', displayName: 'B', defaultValue: false }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(OrTemplate)
|
||||
export class OrExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Boolean(context.evaluateInput(node.id, 'a', false));
|
||||
const b = Boolean(context.evaluateInput(node.id, 'b', false));
|
||||
return { outputs: { result: a || b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Not Node (逻辑非节点)
|
||||
// ============================================================================
|
||||
|
||||
export const NotTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Not',
|
||||
title: 'NOT',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns the opposite boolean value (返回相反的布尔值)',
|
||||
keywords: ['not', '!', 'negate', 'invert', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'bool', displayName: 'Value', defaultValue: false }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(NotTemplate)
|
||||
export class NotExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Boolean(context.evaluateInput(node.id, 'value', false));
|
||||
return { outputs: { result: !value } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// XOR Node (异或节点)
|
||||
// ============================================================================
|
||||
|
||||
export const XorTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Xor',
|
||||
title: 'XOR',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if exactly one of A or B is true (如果 A 和 B 中恰好有一个为 true 则返回 true)',
|
||||
keywords: ['xor', 'exclusive', 'or', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'bool', displayName: 'A', defaultValue: false },
|
||||
{ name: 'b', type: 'bool', displayName: 'B', defaultValue: false }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(XorTemplate)
|
||||
export class XorExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Boolean(context.evaluateInput(node.id, 'a', false));
|
||||
const b = Boolean(context.evaluateInput(node.id, 'b', false));
|
||||
return { outputs: { result: (a || b) && !(a && b) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// NAND Node (与非节点)
|
||||
// ============================================================================
|
||||
|
||||
export const NandTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Nand',
|
||||
title: 'NAND',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if not both A and B are true (如果 A 和 B 不都为 true 则返回 true)',
|
||||
keywords: ['nand', 'not', 'and', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'bool', displayName: 'A', defaultValue: false },
|
||||
{ name: 'b', type: 'bool', displayName: 'B', defaultValue: false }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(NandTemplate)
|
||||
export class NandExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Boolean(context.evaluateInput(node.id, 'a', false));
|
||||
const b = Boolean(context.evaluateInput(node.id, 'b', false));
|
||||
return { outputs: { result: !(a && b) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// In Range Node (范围检查节点)
|
||||
// ============================================================================
|
||||
|
||||
export const InRangeTemplate: BlueprintNodeTemplate = {
|
||||
type: 'InRange',
|
||||
title: 'In Range',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if value is between min and max (如果值在 min 和 max 之间则返回 true)',
|
||||
keywords: ['range', 'between', 'check', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 },
|
||||
{ name: 'min', type: 'float', displayName: 'Min', defaultValue: 0 },
|
||||
{ name: 'max', type: 'float', displayName: 'Max', defaultValue: 1 },
|
||||
{ name: 'inclusive', type: 'bool', displayName: 'Inclusive', defaultValue: true }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(InRangeTemplate)
|
||||
export class InRangeExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
const min = Number(context.evaluateInput(node.id, 'min', 0));
|
||||
const max = Number(context.evaluateInput(node.id, 'max', 1));
|
||||
const inclusive = Boolean(context.evaluateInput(node.id, 'inclusive', true));
|
||||
|
||||
const result = inclusive
|
||||
? value >= min && value <= max
|
||||
: value > min && value < max;
|
||||
|
||||
return { outputs: { result } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Is Null Node (空值检查节点)
|
||||
// ============================================================================
|
||||
|
||||
export const IsNullTemplate: BlueprintNodeTemplate = {
|
||||
type: 'IsNull',
|
||||
title: 'Is Null',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns true if the value is null or undefined (如果值为 null 或 undefined 则返回 true)',
|
||||
keywords: ['null', 'undefined', 'empty', 'check', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'any', displayName: 'Value' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'bool', displayName: 'Is Null' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(IsNullTemplate)
|
||||
export class IsNullExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = context.evaluateInput(node.id, 'value', null);
|
||||
return { outputs: { result: value == null } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Select Node (选择节点)
|
||||
// ============================================================================
|
||||
|
||||
export const SelectTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Select',
|
||||
title: 'Select',
|
||||
category: 'logic',
|
||||
color: '#9C27B0',
|
||||
description: 'Returns A if condition is true, otherwise returns B (如果条件为 true 返回 A,否则返回 B)',
|
||||
keywords: ['select', 'choose', 'ternary', '?:', 'logic'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'condition', type: 'bool', displayName: 'Condition', defaultValue: false },
|
||||
{ name: 'a', type: 'any', displayName: 'A (True)' },
|
||||
{ name: 'b', type: 'any', displayName: 'B (False)' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'any', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(SelectTemplate)
|
||||
export class SelectExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const condition = Boolean(context.evaluateInput(node.id, 'condition', false));
|
||||
const a = context.evaluateInput(node.id, 'a', null);
|
||||
const b = context.evaluateInput(node.id, 'b', null);
|
||||
return { outputs: { result: condition ? a : b } };
|
||||
}
|
||||
}
|
||||
6
packages/framework/blueprint/src/nodes/logic/index.ts
Normal file
6
packages/framework/blueprint/src/nodes/logic/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @zh 逻辑节点 - 比较和逻辑运算节点
|
||||
* @en Logic Nodes - Comparison and logical operation nodes
|
||||
*/
|
||||
|
||||
export * from './ComparisonNodes';
|
||||
@@ -120,3 +120,444 @@ export class DivideExecutor implements INodeExecutor {
|
||||
return { outputs: { result: a / b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Modulo Node (取模节点)
|
||||
// ============================================================================
|
||||
|
||||
export const ModuloTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Modulo',
|
||||
title: 'Modulo',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the remainder of A divided by B (返回 A 除以 B 的余数)',
|
||||
keywords: ['modulo', 'mod', 'remainder', '%', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 1 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(ModuloTemplate)
|
||||
export class ModuloExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 1));
|
||||
if (b === 0) return { outputs: { result: 0 } };
|
||||
return { outputs: { result: a % b } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Absolute Value Node (绝对值节点)
|
||||
// ============================================================================
|
||||
|
||||
export const AbsTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Abs',
|
||||
title: 'Absolute',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the absolute value (返回绝对值)',
|
||||
keywords: ['abs', 'absolute', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(AbsTemplate)
|
||||
export class AbsExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: Math.abs(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Min Node (最小值节点)
|
||||
// ============================================================================
|
||||
|
||||
export const MinTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Min',
|
||||
title: 'Min',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the smaller of two values (返回两个值中较小的一个)',
|
||||
keywords: ['min', 'minimum', 'smaller', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(MinTemplate)
|
||||
export class MinExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: Math.min(a, b) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Max Node (最大值节点)
|
||||
// ============================================================================
|
||||
|
||||
export const MaxTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Max',
|
||||
title: 'Max',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the larger of two values (返回两个值中较大的一个)',
|
||||
keywords: ['max', 'maximum', 'larger', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(MaxTemplate)
|
||||
export class MaxExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: Math.max(a, b) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Clamp Node (限制范围节点)
|
||||
// ============================================================================
|
||||
|
||||
export const ClampTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Clamp',
|
||||
title: 'Clamp',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Clamps a value between min and max (将值限制在最小和最大之间)',
|
||||
keywords: ['clamp', 'limit', 'range', 'bound', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 },
|
||||
{ name: 'min', type: 'float', displayName: 'Min', defaultValue: 0 },
|
||||
{ name: 'max', type: 'float', displayName: 'Max', defaultValue: 1 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(ClampTemplate)
|
||||
export class ClampExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
const min = Number(context.evaluateInput(node.id, 'min', 0));
|
||||
const max = Number(context.evaluateInput(node.id, 'max', 1));
|
||||
return { outputs: { result: Math.max(min, Math.min(max, value)) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Lerp Node (线性插值节点)
|
||||
// ============================================================================
|
||||
|
||||
export const LerpTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Lerp',
|
||||
title: 'Lerp',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Linear interpolation between A and B (A 和 B 之间的线性插值)',
|
||||
keywords: ['lerp', 'interpolate', 'blend', 'mix', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 1 },
|
||||
{ name: 't', type: 'float', displayName: 'Alpha', defaultValue: 0.5 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(LerpTemplate)
|
||||
export class LerpExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 1));
|
||||
const t = Number(context.evaluateInput(node.id, 't', 0.5));
|
||||
return { outputs: { result: a + (b - a) * t } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Random Range Node (随机范围节点)
|
||||
// ============================================================================
|
||||
|
||||
export const RandomRangeTemplate: BlueprintNodeTemplate = {
|
||||
type: 'RandomRange',
|
||||
title: 'Random Range',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns a random number between min and max (返回 min 和 max 之间的随机数)',
|
||||
keywords: ['random', 'range', 'rand', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'min', type: 'float', displayName: 'Min', defaultValue: 0 },
|
||||
{ name: 'max', type: 'float', displayName: 'Max', defaultValue: 1 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(RandomRangeTemplate)
|
||||
export class RandomRangeExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const min = Number(context.evaluateInput(node.id, 'min', 0));
|
||||
const max = Number(context.evaluateInput(node.id, 'max', 1));
|
||||
return { outputs: { result: min + Math.random() * (max - min) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Random Integer Node (随机整数节点)
|
||||
// ============================================================================
|
||||
|
||||
export const RandomIntTemplate: BlueprintNodeTemplate = {
|
||||
type: 'RandomInt',
|
||||
title: 'Random Integer',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns a random integer between min and max inclusive (返回 min 和 max 之间的随机整数,包含边界)',
|
||||
keywords: ['random', 'int', 'integer', 'rand', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'min', type: 'int', displayName: 'Min', defaultValue: 0 },
|
||||
{ name: 'max', type: 'int', displayName: 'Max', defaultValue: 10 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'int', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(RandomIntTemplate)
|
||||
export class RandomIntExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const min = Math.floor(Number(context.evaluateInput(node.id, 'min', 0)));
|
||||
const max = Math.floor(Number(context.evaluateInput(node.id, 'max', 10)));
|
||||
return { outputs: { result: Math.floor(min + Math.random() * (max - min + 1)) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Power Node (幂运算节点)
|
||||
// ============================================================================
|
||||
|
||||
export const PowerTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Power',
|
||||
title: 'Power',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns base raised to the power of exponent (返回底数的指数次幂)',
|
||||
keywords: ['power', 'pow', 'exponent', '^', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'base', type: 'float', displayName: 'Base', defaultValue: 2 },
|
||||
{ name: 'exponent', type: 'float', displayName: 'Exponent', defaultValue: 2 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(PowerTemplate)
|
||||
export class PowerExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const base = Number(context.evaluateInput(node.id, 'base', 2));
|
||||
const exponent = Number(context.evaluateInput(node.id, 'exponent', 2));
|
||||
return { outputs: { result: Math.pow(base, exponent) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Square Root Node (平方根节点)
|
||||
// ============================================================================
|
||||
|
||||
export const SqrtTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Sqrt',
|
||||
title: 'Square Root',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the square root (返回平方根)',
|
||||
keywords: ['sqrt', 'square', 'root', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 4 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(SqrtTemplate)
|
||||
export class SqrtExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 4));
|
||||
return { outputs: { result: Math.sqrt(Math.abs(value)) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Floor Node (向下取整节点)
|
||||
// ============================================================================
|
||||
|
||||
export const FloorTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Floor',
|
||||
title: 'Floor',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Rounds down to the nearest integer (向下取整)',
|
||||
keywords: ['floor', 'round', 'down', 'int', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'int', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(FloorTemplate)
|
||||
export class FloorExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: Math.floor(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Ceil Node (向上取整节点)
|
||||
// ============================================================================
|
||||
|
||||
export const CeilTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Ceil',
|
||||
title: 'Ceil',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Rounds up to the nearest integer (向上取整)',
|
||||
keywords: ['ceil', 'ceiling', 'round', 'up', 'int', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'int', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(CeilTemplate)
|
||||
export class CeilExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: Math.ceil(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Round Node (四舍五入节点)
|
||||
// ============================================================================
|
||||
|
||||
export const RoundTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Round',
|
||||
title: 'Round',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Rounds to the nearest integer (四舍五入到最近的整数)',
|
||||
keywords: ['round', 'int', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'int', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(RoundTemplate)
|
||||
export class RoundExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: Math.round(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Negate Node (取反节点)
|
||||
// ============================================================================
|
||||
|
||||
export const NegateTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Negate',
|
||||
title: 'Negate',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns the negative of a value (返回值的负数)',
|
||||
keywords: ['negate', 'negative', 'minus', '-', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(NegateTemplate)
|
||||
export class NegateExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: -value } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sign Node (符号节点)
|
||||
// ============================================================================
|
||||
|
||||
export const SignTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Sign',
|
||||
title: 'Sign',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Returns -1, 0, or 1 based on the sign of the value (根据值的符号返回 -1、0 或 1)',
|
||||
keywords: ['sign', 'positive', 'negative', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'int', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(SignTemplate)
|
||||
export class SignExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const value = Number(context.evaluateInput(node.id, 'value', 0));
|
||||
return { outputs: { result: Math.sign(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user