Skip to content

Commit 28c6a77

Browse files
committed
math ops and standalone functions (not tested)
1 parent 4aa43f3 commit 28c6a77

File tree

6 files changed

+243
-13
lines changed

6 files changed

+243
-13
lines changed

src/EditorNodes.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import { FillStyle, Theme } from "./colors/Theme";
22
import { UVNode } from "./nodes/attribute/UVNode";
33
import { ValueNode } from "./nodes/input/ValueNode";
4+
import { Node } from "./nodes/Node";
5+
import { mathFunctions, mathOperations } from "./nodes/operators/list";
46
import { MathNode } from "./nodes/operators/MathNode";
7+
import { methodsDefinitions2NodeClassDefinitions } from "./nodes/operators/MethodCallNode";
58
import { MeshStandardNode } from "./nodes/shader/MeshStandardNode";
69
import { ImageTextureNode } from "./nodes/texture/ImageTextureNode";
710
import { WinNode } from "./nodes/WinNode";
811

912
// Define the type for class constructors that extend BaseType
10-
type Constructor<T extends WinNode> = new (...args: any[]) => T;
13+
type Constructor<T extends Node> = new (...args: any[]) => T;
1114

1215
export type NodeGroupType = {
1316
group:string
1417
color:string
1518
exportsScript?:boolean,
16-
nodes:{ TypeClass:Constructor<WinNode>, name:string, id:string }[]
19+
nodes:{
20+
TypeClass:Constructor<Node>,
21+
name:string,
22+
id:string,
23+
constructorArgs?: unknown
24+
}[]
1725
}
1826

1927
export const NodeTypes : NodeGroupType[] = [
@@ -29,15 +37,17 @@ export const NodeTypes : NodeGroupType[] = [
2937
group:"Operators",
3038
color:Theme.config.groupMath as string,
3139
nodes:[
32-
{ TypeClass:MathNode, name:"Math", id:"math-operation"}
40+
//{ TypeClass:MathNode, name:"Math", id:"math-operation"}
41+
...methodsDefinitions2NodeClassDefinitions(mathOperations),
42+
...methodsDefinitions2NodeClassDefinitions(mathFunctions, true),
3343
]
3444
},
3545
{
3646
group:"Shader",
3747
color:Theme.config.groupShader as string,
3848
exportsScript: true,
3949
nodes:[
40-
{ TypeClass:MeshStandardNode, name:"Mesh Standard", id:"mesh-standard-shader"}
50+
{ TypeClass:MeshStandardNode, name:"Mesh Standard Material", id:"mesh-standard-shader"}
4151
]
4252
},
4353
{
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { refract } from "three/tsl";
2+
import { Theme } from "../../colors/Theme";
3+
import { DraggableNumber } from "../../components/DraggableNumber";
4+
import { TextLabel } from "../../components/TextLabel";
5+
import { Column } from "../../layout/Layout";
6+
import { LayoutElement } from "../../layout/LayoutElement";
7+
import { InputOrValue } from "../../properties/InputOrValue";
8+
import { Output } from "../../properties/Output";
9+
import { Node } from "../Node";
10+
import { WinNode } from "../WinNode";
11+
import { MethodCallDef } from "./list";
12+
import { DraggableValue } from "../../components/DraggableValue";
13+
import { Script } from "../../export/Script";
14+
15+
/**
16+
* Utility function to create the nodes defintions required by `EditorNodes`
17+
*/
18+
export function methodsDefinitions2NodeClassDefinitions( list:MethodCallDef[], listAreStandaloneFunctions = false ) {
19+
return list.map( methodDef => ({
20+
21+
TypeClass:MethodCallNode,
22+
name:methodDef.name,
23+
id:methodDef.name,
24+
constructorArgs: {
25+
...methodDef,
26+
isStandalone: listAreStandaloneFunctions
27+
}
28+
29+
}) )
30+
}
31+
32+
/**
33+
* A node that calls a method on it's main input and optionally uses secondary inputs as parameters.
34+
*/
35+
export class MethodCallNode extends WinNode {
36+
private A?:InputOrValue;
37+
private B?:InputOrValue;
38+
private C?:InputOrValue;
39+
40+
constructor( protected config:MethodCallDef ) {
41+
42+
const childs :InputOrValue[] = [];
43+
44+
if( config.params>0 ) childs.push( new InputOrValue(0,"", ()=>this.update()));
45+
if( config.params>1 ) childs.push( new InputOrValue(0,"", ()=>this.update()));
46+
if( config.params==3 ) childs.push( new InputOrValue(0,{
47+
asBar:true,
48+
min:0,
49+
max:1,
50+
step:0.01,
51+
label:"Fac"
52+
}, ()=>this.update()));
53+
54+
super(config.name, Theme.config.groupMath, [
55+
new Output("Result", 0) ,
56+
...childs
57+
]);
58+
59+
[this.A=undefined, this.B = undefined, this.C = undefined] = childs;
60+
}
61+
62+
override width(ctx: CanvasRenderingContext2D): number {
63+
return super.width(ctx) / ( this.config.params==3? 1 : 2)
64+
}
65+
66+
override get nodeName(): string {
67+
return this.config.name+"_call";
68+
}
69+
70+
override writeScript(script: Script): string {
71+
72+
const methodName = this.config.name;
73+
74+
script.importModule( methodName );
75+
76+
const a = this.A?.writeScript( script );
77+
const b = this.B?.writeScript( script );
78+
const c = this.C?.writeScript( script );
79+
80+
let params = [a,b,c];
81+
82+
if( !this.config.isStandalone ){
83+
params.shift();
84+
}
85+
86+
params = params.filter( p=> p !== undefined && p !== null );
87+
88+
return script.define( this.nodeName, (this.config.isStandalone? "" : `${a}.`) + `${methodName}( ${params})` );
89+
}
90+
}

src/nodes/operators/list.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
export type MethodCallDef = {
2+
name:string
3+
params:number
4+
desc:string
5+
symbol?:string
6+
7+
/**
8+
* If true it means the name is the name of a function, not the name of a method in the 1st input.
9+
*/
10+
isStandalone?:boolean
11+
}
12+
13+
export const mathOperations :MethodCallDef[] = [
14+
{ "name": "add", "params": 2, "desc": "Return the addition of two or more value.", "symbol": "+" },
15+
{ "name": "sub", "params": 2, "desc": "Return the subtraction of two or more value.", "symbol": "−" },
16+
{ "name": "mul", "params": 2, "desc": "Return the multiplication of two or more value.", "symbol": "×" },
17+
{ "name": "div", "params": 2, "desc": "Return the division of two or more value.", "symbol": "÷" },
18+
{ "name": "assign", "params": 2, "desc": "Assign one or more value to a and return the same.", "symbol": "=" },
19+
{ "name": "mod", "params": 2, "desc": "Computes the remainder of dividing the first node by the second.", "symbol": "%" },
20+
{ "name": "equal", "params": 2, "desc": "Checks if two nodes are equal.", "symbol": "==" },
21+
{ "name": "notEqual", "params": 2, "desc": "Checks if two nodes are not equal.", "symbol": "≠" },
22+
{ "name": "lessThan", "params": 2, "desc": "Checks if the first node is less than the second.", "symbol": "<" },
23+
{ "name": "greaterThan", "params": 2, "desc": "Checks if the first node is greater than the second.", "symbol": ">" },
24+
{ "name": "lessThanEqual", "params": 2, "desc": "Checks if the first node is less than or equal to the second.", "symbol": "≤" },
25+
{ "name": "greaterThanEqual", "params": 2, "desc": "Checks if the first node is greater than or equal to the second.", "symbol": "≥" },
26+
{ "name": "and", "params": 2, "desc": "Performs logical AND on two nodes.", "symbol": "&&" },
27+
{ "name": "or", "params": 2, "desc": "Performs logical OR on two nodes.", "symbol": "||" },
28+
{ "name": "not", "params": 1, "desc": "Performs logical NOT on a node.", "symbol": "!" },
29+
{ "name": "xor", "params": 2, "desc": "Performs logical XOR on two nodes.", "symbol": "⊕" },
30+
{ "name": "bitAnd", "params": 2, "desc": "Performs bitwise AND on two nodes.", "symbol": "&" },
31+
{ "name": "bitNot", "params": 1, "desc": "Performs bitwise NOT on a node.", "symbol": "~" },
32+
{ "name": "bitOr", "params": 2, "desc": "Performs bitwise OR on two nodes.", "symbol": "|" },
33+
{ "name": "bitXor", "params": 2, "desc": "Performs bitwise XOR on two nodes.", "symbol": "^" },
34+
{ "name": "shiftLeft", "params": 2, "desc": "Shifts a node to the left.", "symbol": "<<" },
35+
{ "name": "shiftRight", "params": 2, "desc": "Shifts a node to the right.", "symbol": ">>" }
36+
];
37+
38+
export const mathFunctions :MethodCallDef[] = [
39+
{ "name": "EPSILON", "params": 0, "desc": "A small value used to handle floating-point precision errors." },
40+
{ "name": "INFINITY", "params": 0, "desc": "Represent infinity." },
41+
{ "name": "abs", "params": 1, "desc": "Return the absolute value of the parameter." },
42+
{ "name": "acos", "params": 1, "desc": "Return the arccosine of the parameter." },
43+
{ "name": "all", "params": 1, "desc": "Return true if all components of x are true." },
44+
{ "name": "any", "params": 1, "desc": "Return true if any component of x is true." },
45+
{ "name": "asin", "params": 1, "desc": "Return the arcsine of the parameter." },
46+
{ "name": "atan", "params": 2, "desc": "Return the arc-tangent of the parameters." },
47+
{ "name": "bitcast", "params": 2, "desc": "Reinterpret the bits of a value as a different type." },
48+
{ "name": "cbrt", "params": 1, "desc": "Return the cube root of the parameter." },
49+
{ "name": "ceil", "params": 1, "desc": "Find the nearest integer that is greater than or equal to the parameter." },
50+
{ "name": "clamp", "params": 3, "desc": "Constrain a value to lie between two further values." },
51+
{ "name": "cos", "params": 1, "desc": "Return the cosine of the parameter." },
52+
{ "name": "cross", "params": 2, "desc": "Calculate the cross product of two vectors." },
53+
{ "name": "dFdx", "params": 1, "desc": "Return the partial derivative of an argument with respect to x." },
54+
{ "name": "dFdy", "params": 1, "desc": "Return the partial derivative of an argument with respect to y." },
55+
{ "name": "degrees", "params": 1, "desc": "Convert a quantity in radians to degrees." },
56+
{ "name": "difference", "params": 2, "desc": "Calculate the absolute difference between two values." },
57+
{ "name": "distance", "params": 2, "desc": "Calculate the distance between two points." },
58+
{ "name": "dot", "params": 2, "desc": "Calculate the dot product of two vectors." },
59+
{ "name": "equals", "params": 2, "desc": "Return true if x equals y." },
60+
{ "name": "exp", "params": 1, "desc": "Return the natural exponentiation of the parameter." },
61+
{ "name": "exp2", "params": 1, "desc": "Return 2 raised to the power of the parameter." },
62+
{ "name": "faceforward", "params": 3, "desc": "Return a vector pointing in the same direction as another." },
63+
{ "name": "floor", "params": 1, "desc": "Find the nearest integer less than or equal to the parameter." },
64+
{ "name": "fract", "params": 1, "desc": "Compute the fractional part of the argument." },
65+
{ "name": "fwidth", "params": 1, "desc": "Return the sum of the absolute derivatives in x and y." },
66+
{ "name": "inverseSqrt", "params": 1, "desc": "Return the inverse of the square root of the parameter." },
67+
{ "name": "invert", "params": 1, "desc": "Invert an alpha parameter ( 1. - x )." },
68+
{ "name": "length", "params": 1, "desc": "Calculate the length of a vector." },
69+
{ "name": "lengthSq", "params": 1, "desc": "Calculate the squared length of a vector." },
70+
{ "name": "log", "params": 1, "desc": "Return the natural logarithm of the parameter." },
71+
{ "name": "log2", "params": 1, "desc": "Return the base 2 logarithm of the parameter." },
72+
{ "name": "max", "params": 2, "desc": "Return the greater of two values." },
73+
{ "name": "min", "params": 2, "desc": "Return the lesser of two values." },
74+
{ "name": "mix", "params": 3, "desc": "Linearly interpolate between two values." },
75+
{ "name": "negate", "params": 1, "desc": "Negate the value of the parameter ( -x )." },
76+
{ "name": "normalize", "params": 1, "desc": "Calculate the unit vector in the same direction as the original vector." },
77+
{ "name": "oneMinus", "params": 1, "desc": "Return 1 minus the parameter." },
78+
{ "name": "pow", "params": 2, "desc": "Return the value of the first parameter raised to the power of the second." },
79+
{ "name": "pow2", "params": 1, "desc": "Return the square of the parameter." },
80+
{ "name": "pow3", "params": 1, "desc": "Return the cube of the parameter." },
81+
{ "name": "pow4", "params": 1, "desc": "Return the fourth power of the parameter." },
82+
{ "name": "radians", "params": 1, "desc": "Convert a quantity in degrees to radians." },
83+
{ "name": "reciprocal", "params": 1, "desc": "Return the reciprocal of the parameter (1/x)." },
84+
{ "name": "reflect", "params": 2, "desc": "Calculate the reflection direction for an incident vector." },
85+
{ "name": "refract", "params": 3, "desc": "Calculate the refraction direction for an incident vector." },
86+
{ "name": "round", "params": 1, "desc": "Round the parameter to the nearest integer." },
87+
{ "name": "saturate", "params": 1, "desc": "Constrain a value between 0 and 1." },
88+
{ "name": "sign", "params": 1, "desc": "Extract the sign of the parameter." },
89+
{ "name": "sin", "params": 1, "desc": "Return the sine of the parameter." },
90+
{ "name": "smoothstep", "params": 3, "desc": "Perform Hermite interpolation between two values." },
91+
{ "name": "sqrt", "params": 1, "desc": "Return the square root of the parameter." },
92+
{ "name": "step", "params": 2, "desc": "Generate a step function by comparing two values." },
93+
{ "name": "tan", "params": 1, "desc": "Return the tangent of the parameter." },
94+
{ "name": "transformDirection", "params": 2, "desc": "Transform the direction of a vector by a matrix and then normalize the result." },
95+
{ "name": "trunc", "params": 1, "desc": "Truncate the parameter, removing the fractional part." }
96+
]

src/properties/InputOrValue.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,37 @@ import { Layout, Row } from "../layout/Layout";
55
import { BasicInputProperty } from "./BasicInputProperty";
66
import { Input } from "./Input";
77

8+
type Config = {
9+
min:number
10+
max:number
11+
step:number
12+
label:string
13+
asBar:boolean
14+
}
15+
816
export class InputOrValue extends BasicInputProperty
917
{
1018
protected notConnectedContent:Layout;
1119
protected valueSlider:DraggableValue;
1220

13-
constructor( size:OutletSize, label:string, protected onChange?:(v?:number)=>void ) {
14-
super( size, label );
21+
constructor( size:OutletSize, label:string|Partial<Config>, protected onChange?:(v?:number)=>void ) {
22+
23+
const hasConfig = typeof label!="string";
24+
25+
const valConfig :Config = {
26+
min: Number.MIN_SAFE_INTEGER,
27+
max: Number.MAX_SAFE_INTEGER,
28+
step: 0.01,
29+
label: "",
30+
asBar: false,
31+
...( !hasConfig?{}:label )
32+
}
33+
34+
const lbl = !hasConfig? label : label.label;
35+
36+
super( size, valConfig.label );
1537

16-
this.valueSlider = new DraggableValue( label, false, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, 0.1, value => this.onChange?.(value) );
38+
this.valueSlider = new DraggableValue( valConfig.label, valConfig.asBar, valConfig.min, valConfig.max, valConfig.step, value => this.onChange?.(value) );
1739

1840
this.notConnectedContent = new Row([
1941
this.valueSlider

src/ui/NodeSelectionModal.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
background-color: black;
55
position: fixed;
66
overflow-y: auto;
7-
z-index: 9;
7+
z-index: 999;
88
padding: 10px;
99
border-radius: 5px;
1010
}

src/ui/NodeSelectionModal.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { NodeTypes } from "../EditorNodes";
2-
import { WinNode } from "../nodes/WinNode";
2+
import { Node } from "../nodes/Node";
33
import styles from "./NodeSelectionModal.module.css";
44

5-
type NodeHandler = (node:WinNode)=>void
5+
type NodeHandler = (node:Node)=>void
66

77
class NodeSelector {
88
private div:HTMLDivElement;
@@ -64,9 +64,21 @@ class NodeSelector {
6464

6565
groupLabel.classList.add(styles.groupTitle)
6666
groupLabel.style.backgroundColor = groupType.color;
67-
groupLabel.innerText = groupType.group;
67+
groupLabel.innerText = groupType.group;
6868

69-
li.addEventListener("click", ev=>this.addNewNode(new node.TypeClass))
69+
li.addEventListener("click", ev=>{
70+
71+
if( node.constructorArgs )
72+
{
73+
this.addNewNode(new node.TypeClass(...(Array.isArray(node.constructorArgs) ? node.constructorArgs : [node.constructorArgs])))
74+
}
75+
else
76+
{
77+
this.addNewNode(new node.TypeClass)
78+
}
79+
80+
81+
})
7082

7183
});
7284

@@ -96,7 +108,7 @@ class NodeSelector {
96108
})
97109
}
98110

99-
private addNewNode( node:WinNode )
111+
private addNewNode( node:Node )
100112
{
101113
this._onCreated?.(node);
102114
this.hide();

0 commit comments

Comments
 (0)