@kiberon-labs/behave-graph-scene 1.0.0

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.
Files changed (50) hide show
  1. package/package.json +34 -0
  2. package/src/Abstractions/Drivers/DummyScene.ts +79 -0
  3. package/src/Abstractions/IScene.ts +18 -0
  4. package/src/GLTFJson.ts +34 -0
  5. package/src/Nodes/Actions/EaseSceneProperty.ts +162 -0
  6. package/src/Nodes/Actions/SetSceneProperty.ts +35 -0
  7. package/src/Nodes/Events/OnSceneNodeClick.ts +66 -0
  8. package/src/Nodes/Logic/ColorNodes.ts +121 -0
  9. package/src/Nodes/Logic/EulerNodes.ts +115 -0
  10. package/src/Nodes/Logic/Mat3Nodes.ts +202 -0
  11. package/src/Nodes/Logic/Mat4Nodes.ts +257 -0
  12. package/src/Nodes/Logic/QuatNodes.ts +178 -0
  13. package/src/Nodes/Logic/Vec2Nodes.ts +111 -0
  14. package/src/Nodes/Logic/Vec3Nodes.ts +121 -0
  15. package/src/Nodes/Logic/Vec4Nodes.ts +112 -0
  16. package/src/Nodes/Logic/VecElements.ts +34 -0
  17. package/src/Nodes/Queries/GetSceneProperty.ts +35 -0
  18. package/src/Values/ColorValue.ts +22 -0
  19. package/src/Values/EulerValue.ts +22 -0
  20. package/src/Values/Internal/Mat2.ts +214 -0
  21. package/src/Values/Internal/Mat3.ts +422 -0
  22. package/src/Values/Internal/Mat4.ts +831 -0
  23. package/src/Values/Internal/Vec2.ts +97 -0
  24. package/src/Values/Internal/Vec3.ts +244 -0
  25. package/src/Values/Internal/Vec4.ts +350 -0
  26. package/src/Values/Mat3Value.ts +20 -0
  27. package/src/Values/Mat4Value.ts +20 -0
  28. package/src/Values/QuatValue.ts +22 -0
  29. package/src/Values/Vec2Value.ts +14 -0
  30. package/src/Values/Vec3Value.ts +22 -0
  31. package/src/Values/Vec4Value.ts +21 -0
  32. package/src/buildScene.ts +479 -0
  33. package/src/index.ts +38 -0
  34. package/src/loadScene.ts +81 -0
  35. package/src/registerSceneProfile.ts +105 -0
  36. package/tests/graphs/logic/Color.json +53 -0
  37. package/tests/graphs/logic/Euler.json +53 -0
  38. package/tests/graphs/logic/Quaternion.json +56 -0
  39. package/tests/graphs/logic/Vector2.json +50 -0
  40. package/tests/graphs/logic/Vector3.json +53 -0
  41. package/tests/graphs/logic/Vector4.json +56 -0
  42. package/tests/readSceneGraphs.test.ts +57 -0
  43. package/tests/registerSceneProfile.test.ts +62 -0
  44. package/tests/tsconfig.json +11 -0
  45. package/tests/values/internal/Vec2.test.ts +74 -0
  46. package/tests/values/internal/Vec3.test.ts +83 -0
  47. package/tests/values/internal/Vec4.test.ts +91 -0
  48. package/tsconfig.json +55 -0
  49. package/tsdown.config.ts +13 -0
  50. package/vitest.config.ts +15 -0
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@kiberon-labs/behave-graph-scene",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "types": "./dist/index.d.ts",
6
+ "main": "./dist/index.js",
7
+ "source": "./src/index.ts",
8
+ "scripts": {
9
+ "build": "tsdown",
10
+ "check": "oxfmt --check",
11
+ "format": "oxfmt",
12
+ "test": "vitest",
13
+ "dev": "tsdown --watch src",
14
+ "lint": "oxlint",
15
+ "typecheck": "tsc"
16
+ },
17
+ "devDependencies": {
18
+ "@types/glob": "^8.0.0",
19
+ "@types/node": "^24.10.1",
20
+ "@types/offscreencanvas": "^2019.7.0",
21
+ "@types/three": "0.152.1",
22
+ "oxfmt": "^0.14.0",
23
+ "oxlint": "^1.29.0",
24
+ "tsdown": "^0.16.5",
25
+ "vitest": "^4.0.10"
26
+ },
27
+ "dependencies": {},
28
+ "license": "ISC",
29
+ "peerDependencies": {
30
+ "@kiberon-labs/behave-graph": "*",
31
+ "three": "0.152.2",
32
+ "three-stdlib": "^2.22.4"
33
+ }
34
+ }
@@ -0,0 +1,79 @@
1
+ import {
2
+ BooleanValue,
3
+ EventEmitter,
4
+ FloatValue,
5
+ IntegerValue,
6
+ StringValue,
7
+ type ValueType
8
+ } from '@kiberon-labs/behave-graph';
9
+
10
+ import { ColorValue } from '../../Values/ColorValue.js';
11
+ import { EulerValue } from '../../Values/EulerValue.js';
12
+ import { QuatValue } from '../../Values/QuatValue.js';
13
+ import { Vec2Value } from '../../Values/Vec2Value.js';
14
+ import { Vec3Value } from '../../Values/Vec3Value.js';
15
+ import { Vec4Value } from '../../Values/Vec4Value.js';
16
+ import type { IScene } from '../IScene.js';
17
+
18
+ export class DummyScene implements IScene {
19
+ public onSceneChanged = new EventEmitter<void>();
20
+
21
+ private valueRegistry: Record<string, ValueType>;
22
+
23
+ constructor() {
24
+ this.valueRegistry = Object.fromEntries(
25
+ [
26
+ BooleanValue,
27
+ StringValue,
28
+ IntegerValue,
29
+ FloatValue,
30
+ Vec2Value,
31
+ Vec3Value,
32
+ Vec4Value,
33
+ ColorValue,
34
+ EulerValue,
35
+ QuatValue
36
+ ].map((valueType) => [valueType.name, valueType])
37
+ );
38
+ // pull in value type nodes
39
+ }
40
+
41
+ getProperty(jsonPath: string, valueTypeName: string): any {
42
+ return this.valueRegistry[valueTypeName]?.creator();
43
+ }
44
+ setProperty(): void {
45
+ this.onSceneChanged.emit();
46
+ }
47
+ addOnClickedListener(
48
+ jsonPath: string,
49
+ callback: (jsonPath: string) => void
50
+ ): void {
51
+ console.log('added on clicked listener');
52
+ }
53
+ removeOnClickedListener(
54
+ jsonPath: string,
55
+ callback: (jsonPath: string) => void
56
+ ): void {
57
+ console.log('removed on clicked listener');
58
+ }
59
+
60
+ getQueryableProperties() {
61
+ return [];
62
+ }
63
+
64
+ getRaycastableProperties() {
65
+ return [];
66
+ }
67
+
68
+ getProperties() {
69
+ return [];
70
+ }
71
+
72
+ addOnSceneChangedListener() {
73
+ console.log('added on scene changed listener');
74
+ }
75
+
76
+ removeOnSceneChangedListener(): void {
77
+ console.log('removed on scene changed listener');
78
+ }
79
+ }
@@ -0,0 +1,18 @@
1
+ import type { Choices } from '@kiberon-labs/behave-graph';
2
+
3
+ export interface IScene {
4
+ getProperty(jsonPath: string, valueTypeName: string): any;
5
+ setProperty(jsonPath: string, valueTypeName: string, value: any): void;
6
+ addOnClickedListener(
7
+ jsonPath: string,
8
+ callback: (jsonPath: string) => void
9
+ ): void;
10
+ removeOnClickedListener(
11
+ jsonPath: string,
12
+ callback: (jsonPath: string) => void
13
+ ): void;
14
+ getRaycastableProperties: () => Choices;
15
+ getProperties: () => Choices;
16
+ addOnSceneChangedListener(listener: () => void): void;
17
+ removeOnSceneChangedListener(listener: () => void): void;
18
+ }
@@ -0,0 +1,34 @@
1
+ export type GLTFAssetJson = {
2
+ generator: string;
3
+ version: string;
4
+ };
5
+
6
+ export type GLTFSceneJson = {
7
+ name: string;
8
+ nodes: number[];
9
+ };
10
+
11
+ export type GLTFNodeJson = {
12
+ name?: string;
13
+ mesh?: number;
14
+ translation?: number[];
15
+ children?: number[];
16
+ };
17
+
18
+ export type GLTFMaterialJson = {
19
+ name?: string;
20
+ doubleSided?: boolean;
21
+ };
22
+
23
+ export type GLTFMeshJson = {
24
+ name?: string;
25
+ };
26
+
27
+ export type GLTFJson = {
28
+ asset: GLTFAssetJson;
29
+ scene: number;
30
+ scenes: GLTFSceneJson[];
31
+ nodes: GLTFNodeJson[];
32
+ materials: GLTFMaterialJson[];
33
+ meshes: GLTFMeshJson[];
34
+ };
@@ -0,0 +1,162 @@
1
+ import {
2
+ AsyncNode,
3
+ type Easing,
4
+ EasingFunctions,
5
+ EasingModes,
6
+ Engine,
7
+ type IGraph,
8
+ type ILifecycleEventEmitter,
9
+ NodeDescription,
10
+ Socket,
11
+ toCamelCase
12
+ } from '@kiberon-labs/behave-graph';
13
+
14
+ import type { IScene } from '../../Abstractions/IScene.js';
15
+
16
+ export class EaseSceneProperty extends AsyncNode {
17
+ public static GetDescriptions(
18
+ scene: IScene,
19
+ lifecycleEventEmitter: ILifecycleEventEmitter,
20
+ ...valueTypeNames: string[]
21
+ ) {
22
+ return valueTypeNames.map(
23
+ (valueTypeName) =>
24
+ new NodeDescription(
25
+ `scene/ease/${valueTypeName}`,
26
+ 'Action',
27
+ `Ease Scene ${toCamelCase(valueTypeName)}`,
28
+ (description, graph, config, id) =>
29
+ new EaseSceneProperty(
30
+ description,
31
+ graph,
32
+ valueTypeName,
33
+ scene,
34
+ lifecycleEventEmitter,
35
+ id
36
+ )
37
+ )
38
+ );
39
+ }
40
+
41
+ public readonly valueTypeName: string;
42
+ private readonly scene: IScene;
43
+ private readonly lifecycleEventEmitter: ILifecycleEventEmitter;
44
+
45
+ constructor(
46
+ description: NodeDescription,
47
+ graph: IGraph,
48
+ valueTypeName: string,
49
+ scene: IScene,
50
+ lifecycleEventEmitter: ILifecycleEventEmitter,
51
+ id: string
52
+ ) {
53
+ super(
54
+ description,
55
+ graph,
56
+ [
57
+ new Socket('flow', 'flow'),
58
+ new Socket('string', 'jsonPath'),
59
+ new Socket(valueTypeName, 'value'),
60
+ new Socket(
61
+ 'string',
62
+ 'easingFunction',
63
+ 'linear',
64
+ undefined,
65
+ Object.keys(EasingFunctions)
66
+ ),
67
+ new Socket(
68
+ 'string',
69
+ 'easingMode',
70
+ 'inOut',
71
+ undefined,
72
+ Object.keys(EasingModes)
73
+ ),
74
+ new Socket('float', 'easeDuration'),
75
+ new Socket('flow', 'cancel')
76
+ ],
77
+ [new Socket('flow', 'flow')],
78
+ {},
79
+ id
80
+ );
81
+ this.valueTypeName = valueTypeName;
82
+ this.scene = scene;
83
+ this.lifecycleEventEmitter = lifecycleEventEmitter;
84
+ }
85
+
86
+ private initialValue: any = undefined;
87
+ private targetValue: any = undefined;
88
+ private duration = 0;
89
+ private elapsedDuration = 0;
90
+ private easing: Easing = EasingFunctions['linear'];
91
+ private startTime = 0;
92
+ private onTick: (() => void) | undefined = undefined;
93
+
94
+ override triggered(
95
+ engine: Engine,
96
+ triggeringSocketName: string,
97
+ finished: () => void
98
+ ) {
99
+ if (triggeringSocketName === 'cancel') {
100
+ this.dispose();
101
+ finished();
102
+ return;
103
+ }
104
+
105
+ // if existing ease in progress, do nothing
106
+ if (this.elapsedDuration >= this.duration) {
107
+ return;
108
+ }
109
+
110
+ this.initialValue = this.scene.getProperty(
111
+ this.readInput('jsonPath'),
112
+ this.valueTypeName
113
+ );
114
+ this.targetValue = this.readInput('value');
115
+ this.duration = this.readInput<number>('duration');
116
+ this.elapsedDuration = 0;
117
+ this.startTime = Date.now();
118
+
119
+ const easingFunction =
120
+ EasingFunctions[
121
+ this.readInput('easingFunction') as keyof typeof EasingFunctions
122
+ ];
123
+ const easingMode =
124
+ EasingModes[this.readInput('easingMode') as keyof typeof EasingModes];
125
+ this.easing = easingMode(easingFunction);
126
+
127
+ const updateOnTick = () => {
128
+ const valueType = this.graph.values[this.valueTypeName]!;
129
+ this.elapsedDuration = (Date.now() - this.startTime) / 1000;
130
+
131
+ const t = Math.min(this.elapsedDuration / this.duration, 1);
132
+ const easedValue = valueType.lerp(
133
+ this.initialValue,
134
+ this.targetValue,
135
+ this.easing(t)
136
+ );
137
+
138
+ this.scene.setProperty(
139
+ this.readInput('jsonPath'),
140
+ this.valueTypeName,
141
+ easedValue
142
+ );
143
+
144
+ if (this.elapsedDuration >= this.duration) {
145
+ this.dispose();
146
+ engine.commitToNewFiber(this, 'flow');
147
+ finished();
148
+ }
149
+ };
150
+
151
+ this.onTick = updateOnTick;
152
+ this.lifecycleEventEmitter.tickEvent.addListener(this.onTick);
153
+ }
154
+
155
+ override dispose() {
156
+ this.elapsedDuration = this.duration = 0;
157
+ if (this.onTick !== undefined) {
158
+ this.lifecycleEventEmitter.tickEvent.removeListener(this.onTick);
159
+ this.onTick = undefined;
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ makeFlowNodeDefinition,
3
+ NodeCategory
4
+ } from '@kiberon-labs/behave-graph';
5
+
6
+ import type { IScene } from '../../Abstractions/IScene.js';
7
+
8
+ export const SetSceneProperty = (valueTypeNames: string[]) =>
9
+ valueTypeNames.map((valueTypeName) =>
10
+ makeFlowNodeDefinition({
11
+ typeName: `scene/set/${valueTypeName}`,
12
+ category: NodeCategory.Effect,
13
+ label: `Set Scene ${valueTypeName}`,
14
+ in: {
15
+ jsonPath: (_, graphApi) => {
16
+ const scene = graphApi.getDependency<IScene>('IScene');
17
+ return {
18
+ valueType: 'string',
19
+ choices: scene?.getProperties()
20
+ };
21
+ },
22
+ value: valueTypeName,
23
+ flow: 'flow'
24
+ },
25
+ out: {
26
+ flow: 'flow'
27
+ },
28
+ initialState: undefined,
29
+ triggered: ({ commit, read, graph }) => {
30
+ const scene = graph.getDependency<IScene>('IScene');
31
+ scene?.setProperty(read('jsonPath'), valueTypeName, read('value'));
32
+ commit('flow');
33
+ }
34
+ })
35
+ );
@@ -0,0 +1,66 @@
1
+ import {
2
+ Assert,
3
+ makeEventNodeDefinition,
4
+ NodeCategory
5
+ } from '@kiberon-labs/behave-graph';
6
+
7
+ import type { IScene } from '../../Abstractions/IScene.js';
8
+
9
+ type State = {
10
+ jsonPath?: string | undefined;
11
+ handleNodeClick?: ((jsonPath: string) => void) | undefined;
12
+ };
13
+
14
+ const initialState = (): State => ({});
15
+
16
+ // very 3D specific.
17
+ export const OnSceneNodeClick = makeEventNodeDefinition({
18
+ typeName: 'scene/nodeClick',
19
+ category: NodeCategory.Event,
20
+ label: 'On Scene Node Click',
21
+ in: {
22
+ jsonPath: (_, graphApi) => {
23
+ const scene = graphApi.getDependency<IScene>('IScene');
24
+
25
+ return {
26
+ valueType: 'string',
27
+ choices: scene?.getRaycastableProperties()
28
+ };
29
+ }
30
+ },
31
+ out: {
32
+ flow: 'flow'
33
+ },
34
+ initialState: initialState(),
35
+ init: ({ read, commit, graph }) => {
36
+ const handleNodeClick = () => {
37
+ commit('flow');
38
+ };
39
+
40
+ const jsonPath = read<string>('jsonPath');
41
+
42
+ const scene = graph.getDependency<IScene>('IScene');
43
+ scene?.addOnClickedListener(jsonPath, handleNodeClick);
44
+
45
+ const state: State = {
46
+ handleNodeClick,
47
+ jsonPath
48
+ };
49
+
50
+ return state;
51
+ },
52
+ dispose: ({
53
+ state: { handleNodeClick, jsonPath },
54
+ graph: { getDependency }
55
+ }) => {
56
+ Assert.mustBeTrue(handleNodeClick !== undefined);
57
+ Assert.mustBeTrue(jsonPath !== undefined);
58
+
59
+ if (!jsonPath || !handleNodeClick) return {};
60
+
61
+ const scene = getDependency<IScene>('scene');
62
+ scene?.removeOnClickedListener(jsonPath, handleNodeClick);
63
+
64
+ return {};
65
+ }
66
+ });
@@ -0,0 +1,121 @@
1
+ import { makeInNOutFunctionDesc } from '@kiberon-labs/behave-graph';
2
+
3
+ import {
4
+ hexToRGB,
5
+ hslToRGB,
6
+ rgbToHex,
7
+ rgbToHSL,
8
+ Vec3,
9
+ vec3Add,
10
+ vec3Equals,
11
+ vec3Mix,
12
+ vec3MultiplyByScalar,
13
+ vec3Negate,
14
+ vec3Subtract
15
+ } from '../../Values/Internal/Vec3.js';
16
+
17
+ export const Constant = makeInNOutFunctionDesc({
18
+ name: 'math/color',
19
+ label: 'Color',
20
+ in: ['color'],
21
+ out: 'color',
22
+ exec: (a: Vec3) => a
23
+ });
24
+
25
+ export const Create = makeInNOutFunctionDesc({
26
+ name: 'math/toColor/rgb',
27
+ label: 'RGB To Color',
28
+ in: [{ r: 'float' }, { g: 'float' }, { b: 'float' }],
29
+ out: 'color',
30
+ exec: (r: number, g: number, b: number) => new Vec3(r, g, b)
31
+ });
32
+
33
+ export const Elements = makeInNOutFunctionDesc({
34
+ name: 'math/toRgb/color',
35
+ label: 'Color to RGB',
36
+ in: ['color'],
37
+ out: [{ r: 'float' }, { g: 'float' }, { b: 'float' }],
38
+ exec: (a: Vec3) => {
39
+ return { r: a.x, g: a.y, b: a.z };
40
+ }
41
+ });
42
+
43
+ export const Add = makeInNOutFunctionDesc({
44
+ name: 'math/add/color',
45
+ label: '+',
46
+ in: ['color', 'color'],
47
+ out: 'color',
48
+ exec: vec3Add
49
+ });
50
+
51
+ export const Subtract = makeInNOutFunctionDesc({
52
+ name: 'math/subtract/color',
53
+ label: '-',
54
+ in: ['color', 'color'],
55
+ out: 'color',
56
+ exec: vec3Subtract
57
+ });
58
+
59
+ export const Negate = makeInNOutFunctionDesc({
60
+ name: 'math/negate/color',
61
+ label: '-',
62
+ in: ['color'],
63
+ out: 'color',
64
+ exec: vec3Negate
65
+ });
66
+
67
+ export const Scale = makeInNOutFunctionDesc({
68
+ name: 'math/scale/color',
69
+ label: '×',
70
+ in: ['color', 'float'],
71
+ out: 'color',
72
+ exec: vec3MultiplyByScalar
73
+ });
74
+
75
+ export const Mix = makeInNOutFunctionDesc({
76
+ name: 'math/mix/color',
77
+ label: '÷',
78
+ in: [{ a: 'color' }, { b: 'color' }, { t: 'float' }],
79
+ out: 'color',
80
+ exec: vec3Mix
81
+ });
82
+
83
+ export const HslToColor = makeInNOutFunctionDesc({
84
+ name: 'math/ToColor/hsl',
85
+ label: 'HSL to Color',
86
+ in: ['vec3'],
87
+ out: 'color',
88
+ exec: hslToRGB
89
+ });
90
+
91
+ export const ColorToHsl = makeInNOutFunctionDesc({
92
+ name: 'math/toHsl/color',
93
+ label: 'Color to HSL',
94
+ in: ['color'],
95
+ out: 'vec3',
96
+ exec: rgbToHSL
97
+ });
98
+
99
+ export const HexToColor = makeInNOutFunctionDesc({
100
+ name: 'math/toColor/hex',
101
+ label: 'HEX to Color',
102
+ in: ['float'],
103
+ out: 'color',
104
+ exec: hexToRGB
105
+ });
106
+
107
+ export const ColorToHex = makeInNOutFunctionDesc({
108
+ name: 'math/toHex/color',
109
+ label: 'Color to HEX',
110
+ in: ['color'],
111
+ out: 'float',
112
+ exec: rgbToHex
113
+ });
114
+
115
+ export const Equal = makeInNOutFunctionDesc({
116
+ name: 'math/equal/color',
117
+ label: '=',
118
+ in: [{ a: 'color' }, { b: 'color' }, { tolerance: 'float' }],
119
+ out: 'boolean',
120
+ exec: vec3Equals
121
+ });
@@ -0,0 +1,115 @@
1
+ import { makeInNOutFunctionDesc } from '@kiberon-labs/behave-graph';
2
+
3
+ import {
4
+ mat3ToEuler,
5
+ mat4ToEuler,
6
+ quatToEuler,
7
+ Vec3,
8
+ vec3Add,
9
+ vec3Equals,
10
+ vec3Mix,
11
+ vec3MultiplyByScalar,
12
+ vec3Negate,
13
+ vec3Subtract
14
+ } from '../../Values/Internal/Vec3.js';
15
+
16
+ export const Constant = makeInNOutFunctionDesc({
17
+ name: 'math/euler',
18
+ label: 'Euler',
19
+ in: ['euler'],
20
+ out: 'euler',
21
+ exec: (a: Vec3) => a
22
+ });
23
+
24
+ export const Create = makeInNOutFunctionDesc({
25
+ name: 'math/toEuler/float',
26
+ label: 'Float to Euler',
27
+ in: [{ x: 'float' }, { y: 'float' }, { z: 'float' }],
28
+ out: 'euler',
29
+ exec: (x: number, y: number, z: number) => new Vec3(x, y, z)
30
+ });
31
+
32
+ export const Elements = makeInNOutFunctionDesc({
33
+ name: 'math/toFloat/euler',
34
+ label: 'Euler to Float',
35
+ in: ['euler'],
36
+ out: [{ x: 'float' }, { y: 'float' }, { z: 'float' }],
37
+ exec: (a: Vec3) => {
38
+ return { x: a.x, y: a.y, z: a.z };
39
+ }
40
+ });
41
+
42
+ export const Add = makeInNOutFunctionDesc({
43
+ name: 'math/add/euler',
44
+ label: '+',
45
+ in: ['euler', 'euler'],
46
+ out: 'euler',
47
+ exec: vec3Add
48
+ });
49
+
50
+ export const Subtract = makeInNOutFunctionDesc({
51
+ name: 'math/subtract/euler',
52
+ label: '-',
53
+ in: ['euler', 'euler'],
54
+ out: 'euler',
55
+ exec: vec3Subtract
56
+ });
57
+
58
+ export const Negate = makeInNOutFunctionDesc({
59
+ name: 'math/negate/euler',
60
+ label: '-',
61
+ in: ['euler'],
62
+ out: 'euler',
63
+ exec: vec3Negate
64
+ });
65
+
66
+ export const Scale = makeInNOutFunctionDesc({
67
+ name: 'math/scale/euler',
68
+ label: '×',
69
+ in: ['euler', 'float'],
70
+ out: 'euler',
71
+ exec: vec3MultiplyByScalar
72
+ });
73
+
74
+ export const Mix = makeInNOutFunctionDesc({
75
+ name: 'math/mix/euler',
76
+ label: '÷',
77
+ in: [{ a: 'euler' }, { b: 'euler' }, { t: 'float' }],
78
+ out: 'euler',
79
+ exec: (a: Vec3, b: Vec3, t: number) => {
80
+ console.warn('TODO: this is not shortest path');
81
+ return vec3Mix(a, b, t);
82
+ }
83
+ });
84
+
85
+ export const Mat3ToEuler = makeInNOutFunctionDesc({
86
+ name: 'math/toEuler/mat3',
87
+ label: 'To Euler',
88
+ in: ['mat3'],
89
+ out: 'euler',
90
+ exec: mat3ToEuler
91
+ });
92
+
93
+ export const Mat4ToEuler = makeInNOutFunctionDesc({
94
+ name: 'math/toEuler/mat4',
95
+ label: 'To Euler',
96
+ in: ['mat4'],
97
+ out: 'euler',
98
+ exec: mat4ToEuler
99
+ });
100
+
101
+ export const QuatToEuler = makeInNOutFunctionDesc({
102
+ name: 'math/toEuler/quat',
103
+ label: 'To Euler',
104
+ in: ['quat'],
105
+ out: 'euler',
106
+ exec: quatToEuler
107
+ });
108
+
109
+ export const Equal = makeInNOutFunctionDesc({
110
+ name: 'math/equal/euler',
111
+ label: '=',
112
+ in: [{ a: 'euler' }, { b: 'euler' }, { tolerance: 'float' }],
113
+ out: 'boolean',
114
+ exec: vec3Equals
115
+ });