glre 0.32.0 → 0.34.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.
package/src/node/index.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import { code } from './code'
3
3
  import { builtin, conversion as c, function_ as f, uniform as u } from './node'
4
- import { hex2rgb } from './utils'
5
- import type { NodeContext, X } from './types'
4
+ import { hex2rgb, sortHeadersByDependencies } from './utils'
5
+ import type { Constants as C, NodeContext, X, Vec2, Float } from './types'
6
6
  export * from './code'
7
7
  export * from './node'
8
8
  export * from './scope'
@@ -17,7 +17,11 @@ out vec4 fragColor;
17
17
 
18
18
  const generateHead = (x: X, c: NodeContext) => {
19
19
  const body = code(x, c)
20
- const head = Array.from(c.headers!.values()).join('\n')
20
+ let head = ''
21
+ if (c.isWebGL && c.code?.dependencies) {
22
+ const sorted = sortHeadersByDependencies(c.code.headers, c.code.dependencies)
23
+ head = sorted.map(([, value]) => value).join('\n')
24
+ } else head = Array.from(c.code?.headers?.values() || []).join('\n')
21
25
  return [head, body]
22
26
  }
23
27
 
@@ -26,82 +30,84 @@ const generateStruct = (id: string, map: Map<string, string>) => {
26
30
  }
27
31
 
28
32
  export const vertex = (x: X, c: NodeContext) => {
29
- c.headers?.clear()
33
+ if (is.str(x)) return x.trim()
34
+ c.code?.headers?.clear()
30
35
  c.isFrag = false // for varying inputs or outputs
31
36
  const [head, body] = generateHead(x, c)
32
37
  const ret = []
33
38
  if (c.isWebGL) {
34
39
  ret.push('#version 300 es')
35
- for (const code of c.vertInputs!.values()) ret.push(`in ${code}`)
36
- for (const code of c.vertOutputs!.values()) ret.push(`out ${code}`)
40
+ for (const code of c.code?.vertInputs?.values() || []) ret.push(`in ${code}`)
41
+ for (const code of c.code?.vertOutputs?.values() || []) ret.push(`out ${code}`)
37
42
  ret.push(head)
38
43
  ret.push('void main() {')
39
44
  ret.push(` gl_Position = ${body};`)
40
- for (const [id, code] of c.vertVaryings!.entries()) ret.push(` ${id} = ${code};`)
45
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` ${id} = ${code};`)
41
46
  } else {
42
- if (c.vertInputs?.size) ret.push(generateStruct('In', c.vertInputs))
43
- if (c.vertOutputs?.size) ret.push(generateStruct('Out', c.vertOutputs))
47
+ if (c.code?.vertInputs?.size) ret.push(generateStruct('In', c.code.vertInputs))
48
+ if (c.code?.vertOutputs?.size) ret.push(generateStruct('Out', c.code.vertOutputs))
44
49
  ret.push(head)
45
50
  ret.push('@vertex')
46
- ret.push(`fn main(${c.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
51
+ ret.push(`fn main(${c.code?.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
47
52
  ret.push(' var out: Out;')
48
53
  ret.push(` out.position = ${body};`)
49
- for (const [id, code] of c.vertVaryings!.entries()) ret.push(` out.${id} = ${code};`)
54
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` out.${id} = ${code};`)
50
55
  ret.push(' return out;')
51
56
  }
52
57
  ret.push('}')
53
- const main = ret.filter(Boolean).join('\n')
58
+ const main = ret.filter(Boolean).join('\n').trim()
54
59
  console.log(`↓↓↓generated↓↓↓\n${main}`)
55
60
  return main
56
61
  }
57
62
 
58
63
  export const fragment = (x: X, c: NodeContext) => {
59
- c.headers?.clear()
64
+ if (is.str(x)) return x.trim()
65
+ c.code?.headers?.clear()
60
66
  c.isFrag = true // for varying inputs or outputs
61
67
  const [head, body] = generateHead(x, c)
62
68
  const ret = []
63
69
  if (c.isWebGL) {
64
70
  ret.push(GLSL_FRAGMENT_HEAD)
65
- for (const code of c.fragInputs!.values()) ret.push(`in ${code}`)
71
+ for (const code of c.code?.fragInputs?.values() || []) ret.push(`in ${code}`)
66
72
  ret.push(head)
67
73
  ret.push(`void main() {\n fragColor = ${body};`)
68
74
  } else {
69
- if (c.fragInputs?.size) ret.push(generateStruct('Out', c.fragInputs))
75
+ if (c.code?.fragInputs?.size) ret.push(generateStruct('Out', c.code.fragInputs))
70
76
  ret.push(head)
71
77
  ret.push(`@fragment\nfn main(out: Out) -> @location(0) vec4f {`)
72
78
  ret.push(` return ${body};`)
73
79
  }
74
80
  ret.push('}')
75
- const main = ret.filter(Boolean).join('\n')
81
+ const main = ret.filter(Boolean).join('\n').trim()
76
82
  console.log(`↓↓↓generated↓↓↓\n${main}`)
77
83
  return main
78
84
  }
79
85
 
80
86
  // Builtin Variables
81
- export const position = builtin('position')
82
- export const vertexIndex = builtin('vertex_index')
83
- export const instanceIndex = builtin('instance_index')
84
- export const frontFacing = builtin('front_facing')
85
- export const fragDepth = builtin('frag_depth')
86
- export const sampleIndex = builtin('sample_index')
87
- export const sampleMask = builtin('sample_mask')
88
- export const pointCoord = builtin('point_coord')
87
+ export const position = builtin<'vec4'>('position')
88
+ export const vertexIndex = builtin<'uint'>('vertex_index')
89
+ export const instanceIndex = builtin<'uint'>('instance_index')
90
+ export const frontFacing = builtin<'bool'>('front_facing')
91
+ export const fragDepth = builtin<'float'>('frag_depth')
92
+ export const sampleIndex = builtin<'uint'>('sample_index')
93
+ export const sampleMask = builtin<'uint'>('sample_mask')
94
+ export const pointCoord = builtin<'vec2'>('point_coord')
89
95
 
90
96
  // TSL Compatible Builtin Variables
91
- export const normalLocal = builtin('normalLocal')
92
- export const normalWorld = builtin('normalWorld')
93
- export const normalView = builtin('normalView')
94
- export const positionLocal = builtin('position')
95
- export const positionWorld = builtin('positionWorld')
96
- export const positionView = builtin('positionView')
97
- export const screenCoordinate = builtin('screenCoordinate')
98
- export const screenUV = builtin('screenUV')
99
-
100
- // Type constructors
101
- export const float = (x: X) => c('float', x)
102
- export const int = (x: X) => c('int', x)
103
- export const uint = (x: X) => c('uint', x)
104
- export const bool = (x: X) => c('bool', x)
97
+ export const positionLocal = builtin<'vec3'>('position')
98
+ export const positionWorld = builtin<'vec3'>('positionWorld')
99
+ export const positionView = builtin<'vec3'>('positionView')
100
+ export const normalLocal = builtin<'vec3'>('normalLocal')
101
+ export const normalWorld = builtin<'vec3'>('normalWorld')
102
+ export const normalView = builtin<'vec3'>('normalView')
103
+ export const screenCoordinate = builtin<'vec2'>('screenCoordinate')
104
+ export const screenUV = builtin<'vec2'>('screenUV')
105
+
106
+ // Type constructors with proper type inference
107
+ export const float = (x?: X) => c('float', x)
108
+ export const int = (x?: X) => c('int', x)
109
+ export const uint = (x?: X) => c('uint', x)
110
+ export const bool = (x?: X) => c('bool', x)
105
111
  export const vec2 = (x?: X, y?: X) => c('vec2', x, y)
106
112
  export const vec3 = (x?: X, y?: X, z?: X) => c('vec3', x, y, z)
107
113
  export const vec4 = (x?: X, y?: X, z?: X, w?: X) => c('vec4', x, y, z, w)
@@ -117,77 +123,99 @@ export const uvec4 = (x?: X, y?: X, z?: X, w?: X) => c('uvec4', x, y, z, w)
117
123
  export const bvec2 = (x?: X, y?: X) => c('bvec2', x, y)
118
124
  export const bvec3 = (x?: X, y?: X, z?: X) => c('bvec3', x, y, z)
119
125
  export const bvec4 = (x?: X, y?: X, z?: X, w?: X) => c('bvec4', x, y, z, w)
120
- export const texture2D = () => c('texture')
126
+ export const texture2D = (x?: X) => c('texture', x)
121
127
  export const sampler2D = () => c('sampler2D')
122
128
  export const color = (r?: X, g?: X, b?: X) => {
123
129
  if (is.num(r) && is.und(g) && is.und(b)) return vec3(...hex2rgb(r))
124
130
  return vec3(r, g, b)
125
131
  }
126
132
 
127
- // Default uniforms
128
- export const iResolution = u(vec2(1280, 800), 'iResolution')
129
- export const iMouse = u(vec2(0, 0), 'iMouse')
130
- export const iTime = u(float(0), 'iTime')
131
- export const uv = () => position.xy.div(iResolution)
132
-
133
- // Texture Functions
134
- export const texture = (x: X, y: X, z?: X) => f('texture', x, y, z)
135
- export const cubeTexture = (x: X, y: X, z?: X) => f('cubeTexture', x, y, z)
136
- export const textureSize = (x: X, y?: X) => f('textureSize', x, y)
137
-
138
- // Math Functions
139
- export const abs = (x: X) => f('abs', x)
140
- export const acos = (x: X) => f('acos', x)
141
- export const all = (x: X) => f('all', x)
142
- export const any = (x: X) => f('any', x)
143
- export const asin = (x: X) => f('asin', x)
144
- export const atan = (y: X, x?: X) => (x !== undefined ? f('atan', y, x) : f('atan', y))
145
- export const atan2 = (y: X, x: X) => f('atan', y, x)
146
- export const bitcast = (x: X, y: X) => f('bitcast', x, y)
147
- export const cbrt = (x: X) => f('cbrt', x)
148
- export const ceil = (x: X) => f('ceil', x)
149
- export const clamp = (x: X, min: X, max: X) => f('clamp', x, min, max)
150
- export const cos = (x: X) => f('cos', x)
151
- export const cross = (x: X, y: X) => f('cross', x, y)
152
- export const dFdx = (p: X) => f('dFdx', p)
153
- export const dFdy = (p: X) => f('dFdy', p)
154
- export const degrees = (radians: X) => f('degrees', radians)
155
- export const difference = (x: X, y: X) => f('difference', x, y)
156
- export const distance = (x: X, y: X) => f('distance', x, y)
157
- export const dot = (x: X, y: X) => f('dot', x, y)
158
- export const equals = (x: X, y: X) => f('equals', x, y)
159
- export const exp = (x: X) => f('exp', x)
160
- export const exp2 = (x: X) => f('exp2', x)
161
- export const faceforward = (N: X, I: X, Nref: X) => f('faceforward', N, I, Nref)
162
- export const floor = (x: X) => f('floor', x)
163
- export const fract = (x: X) => f('fract', x)
164
- export const fwidth = (x: X) => f('fwidth', x)
165
- export const inverseSqrt = (x: X) => f('inverseSqrt', x)
166
- export const length = (x: X) => f('length', x)
167
- export const lengthSq = (x: X) => f('lengthSq', x)
168
- export const log = (x: X) => f('log', x)
169
- export const log2 = (x: X) => f('log2', x)
170
- export const max = (x: X, y: X) => f('max', x, y)
171
- export const min = (x: X, y: X) => f('min', x, y)
172
- export const mix = (x: X, y: X, a: X) => f('mix', x, y, a)
173
- export const negate = (x: X) => f('negate', x)
174
- export const normalize = (x: X) => f('normalize', x)
175
- export const oneMinus = (x: X) => f('oneMinus', x)
176
- export const pow = (x: X, y: X) => f('pow', x, y)
177
- export const pow2 = (x: X) => f('pow2', x)
178
- export const pow3 = (x: X) => f('pow3', x)
179
- export const pow4 = (x: X) => f('pow4', x)
180
- export const radians = (degrees: X) => f('radians', degrees)
181
- export const reciprocal = (x: X) => f('reciprocal', x)
182
- export const reflect = (I: X, N: X) => f('reflect', I, N)
183
- export const refract = (I: X, N: X, eta: X) => f('refract', I, N, eta)
184
- export const round = (x: X) => f('round', x)
185
- export const saturate = (x: X) => f('saturate', x)
186
- export const sign = (x: X) => f('sign', x)
187
- export const sin = (x: X) => f('sin', x)
188
- export const smoothstep = (e0: X, e1: X, x: X) => f('smoothstep', e0, e1, x)
189
- export const sqrt = (x: X) => f('sqrt', x)
190
- export const step = (edge: X, x: X) => f('step', edge, x)
191
- export const tan = (x: X) => f('tan', x)
192
- export const transformDirection = (dir: X, matrix: X) => f('transformDirection', dir, matrix)
193
- export const trunc = (x: X) => f('trunc', x)
133
+ // Default uniforms with proper typing
134
+ export const iResolution: Vec2 = u(vec2(), 'iResolution')
135
+ export const iMouse: Vec2 = u(vec2(), 'iMouse')
136
+ export const iTime: Float = u(float(), 'iTime')
137
+ export const uv = position.xy.div(iResolution)
138
+
139
+ // Texture Functions - always return vec4
140
+ export const texture = (x: X, y: X, z?: X) => f<'vec4'>('texture', x, y, z)
141
+ export const cubeTexture = (x: X, y: X, z?: X) => f<'vec4'>('cubeTexture', x, y, z)
142
+ export const textureSize = (x: X, y?: X) => f<'vec4'>('textureSize', x, y)
143
+
144
+ // Functions that always return float regardless of input
145
+ export const length = (x: X) => f<'float'>('length', x)
146
+ export const lengthSq = (x: X) => f<'float'>('lengthSq', x)
147
+ export const distance = (x: X, y: X) => f<'float'>('distance', x, y)
148
+ export const dot = (x: X, y: X) => f<'float'>('dot', x, y)
149
+
150
+ // Functions that always return bool
151
+ export const all = <T extends C>(x: X<T>) => f<'bool'>('all', x)
152
+ export const any = <T extends C>(x: X<T>) => f<'bool'>('any', x)
153
+
154
+ // Functions that always return vec3 (cross product only works with vec3)
155
+ export const cross = (x: X<'vec3'>, y: X<'vec3'>) => f<'vec3'>('cross', x, y)
156
+
157
+ // Component-wise functions - preserve input type (T -> T)
158
+ export const abs = <T extends C>(x: X<T>) => f<T>('abs', x)
159
+ export const sign = <T extends C>(x: X<T>) => f<T>('sign', x)
160
+ export const floor = <T extends C>(x: X<T>) => f<T>('floor', x)
161
+ export const ceil = <T extends C>(x: X<T>) => f<T>('ceil', x)
162
+ export const round = <T extends C>(x: X<T>) => f<T>('round', x)
163
+ export const fract = <T extends C>(x: X<T>) => f<T>('fract', x)
164
+ export const trunc = <T extends C>(x: X<T>) => f<T>('trunc', x)
165
+ export const sin = <T extends C>(x: X<T>) => f<T>('sin', x)
166
+ export const cos = <T extends C>(x: X<T>) => f<T>('cos', x)
167
+ export const tan = <T extends C>(x: X<T>) => f<T>('tan', x)
168
+ export const asin = <T extends C>(x: X<T>) => f<T>('asin', x)
169
+ export const acos = <T extends C>(x: X<T>) => f<T>('acos', x)
170
+ export const atan = <T extends C>(x: X<T>) => f<T>('atan', x)
171
+ export const sinh = <T extends C>(x: X<T>) => f<T>('sinh', x)
172
+ export const cosh = <T extends C>(x: X<T>) => f<T>('cosh', x)
173
+ export const tanh = <T extends C>(x: X<T>) => f<T>('tanh', x)
174
+ export const asinh = <T extends C>(x: X<T>) => f<T>('asinh', x)
175
+ export const acosh = <T extends C>(x: X<T>) => f<T>('acosh', x)
176
+ export const atanh = <T extends C>(x: X<T>) => f<T>('atanh', x)
177
+ export const exp = <T extends C>(x: X<T>) => f<T>('exp', x)
178
+ export const exp2 = <T extends C>(x: X<T>) => f<T>('exp2', x)
179
+ export const log = <T extends C>(x: X<T>) => f<T>('log', x)
180
+ export const log2 = <T extends C>(x: X<T>) => f<T>('log2', x)
181
+ export const sqrt = <T extends C>(x: X<T>) => f<T>('sqrt', x)
182
+ export const inverseSqrt = <T extends C>(x: X<T>) => f<T>('inverseSqrt', x)
183
+ export const normalize = <T extends C>(x: X<T>) => f<T>('normalize', x)
184
+ export const oneMinus = <T extends C>(x: X<T>) => f<T>('oneMinus', x)
185
+ export const saturate = <T extends C>(x: X<T>) => f<T>('saturate', x)
186
+ export const negate = <T extends C>(x: X<T>) => f<T>('negate', x)
187
+ export const reciprocal = <T extends C>(x: X<T>) => f<T>('reciprocal', x)
188
+ export const dFdx = <T extends C>(x: X<T>) => f<T>('dFdx', x)
189
+ export const dFdy = <T extends C>(x: X<T>) => f<T>('dFdy', x)
190
+ export const fwidth = <T extends C>(x: X<T>) => f<T>('fwidth', x)
191
+ export const degrees = <T extends C>(x: X<T>) => f<T>('degrees', x)
192
+ export const radians = <T extends C>(x: X<T>) => f<T>('radians', x)
193
+
194
+ // Functions where first argument determines return type
195
+ export const reflect = <T extends C>(I: X<T>, N: X) => f<T>('reflect', I, N)
196
+ export const refract = <T extends C>(I: X<T>, N: X, eta: X) => f<T>('refract', I, N, eta)
197
+
198
+ // Functions with highest priority type among arguments (using first arg for simplicity)
199
+ export const min = <T extends C>(x: X<T>, y: X) => f<T>('min', x, y)
200
+ export const max = <T extends C>(x: X<T>, y: X) => f<T>('max', x, y)
201
+ export const mix = <T extends C>(x: X<T>, y: X, a: X) => f<T>('mix', x, y, a)
202
+ export const clamp = <T extends C>(x: X<T>, minVal: X, maxVal: X) => f<T>('clamp', x, minVal, maxVal)
203
+ export const step = <T extends C>(edge: X, x: X<T>) => f<T>('step', edge, x)
204
+ export const smoothstep = <T extends C>(e0: X, e1: X, x: X<T>) => f<T>('smoothstep', e0, e1, x)
205
+
206
+ // Two-argument functions with highest priority type
207
+ export const atan2 = <T extends C>(y: X<T>, x: X) => f<T>('atan2', y, x)
208
+ export const pow = <T extends C>(x: X<T>, y: X) => f<T>('pow', x, y)
209
+
210
+ // Component-wise power functions
211
+ export const pow2 = <T extends C>(x: X<T>) => f<T>('pow2', x)
212
+ export const pow3 = <T extends C>(x: X<T>) => f<T>('pow3', x)
213
+ export const pow4 = <T extends C>(x: X<T>) => f<T>('pow4', x)
214
+
215
+ // Utility functions
216
+ export const bitcast = <T extends C>(x: X<T>, y: X) => f<T>('bitcast', x, y)
217
+ export const cbrt = <T extends C>(x: X<T>) => f<T>('cbrt', x)
218
+ export const difference = <T extends C>(x: X<T>, y: X) => f<T>('difference', x, y)
219
+ export const equals = (x: X, y: X) => f<'bool'>('equals', x, y)
220
+ export const faceforward = <T extends C>(N: X<T>, I: X, Nref: X) => f<T>('faceforward', N, I, Nref)
221
+ export const transformDirection = <T extends C>(dir: X<T>, matrix: X) => f<T>('transformDirection', dir, matrix)
package/src/node/infer.ts CHANGED
@@ -1,96 +1,91 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import {
3
- BOOL_RETURN_FUNCTIONS,
4
3
  BUILTIN_TYPES,
5
4
  COMPARISON_OPERATORS,
6
5
  COMPONENT_COUNT_TO_TYPE,
7
- CONSTANTS,
8
- FIRST_ARG_TYPE_FUNCTIONS,
9
- HIGHEST_TYPE_FUNCTIONS,
6
+ FUNCTION_RETURN_TYPES,
10
7
  LOGICAL_OPERATORS,
11
- PRESERVE_TYPE_FUNCTIONS,
12
- SCALAR_RETURN_FUNCTIONS,
13
- VEC3_RETURN_FUNCTIONS,
14
- VEC4_RETURN_FUNCTIONS,
15
8
  } from './const'
16
- import { isConstantsType, isNodeProxy } from './utils'
17
- import type { Constants, NodeContext, NodeProxy, X } from './types'
9
+ import { isConstants, isNodeProxy, isSwizzle } from './utils'
10
+ import type { Constants as C, NodeContext, NodeProxy, X } from './types'
18
11
 
19
- const getHighestPriorityType = (args: X[], c: NodeContext) => {
20
- return args.reduce((highest, current) => {
21
- const currentType = infer(current, c)
22
- const highestPriority = CONSTANTS.indexOf(highest as any)
23
- const currentPriority = CONSTANTS.indexOf(currentType as any)
24
- return currentPriority > highestPriority ? currentType : highest
25
- }, 'float') as Constants
12
+ const inferBuiltin = <T extends C>(id: string | undefined) => {
13
+ return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES] as T
26
14
  }
27
15
 
28
- const inferBuiltin = (id: string | undefined): Constants => {
29
- return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES]!
16
+ // Unified logic with types.ts InferOperator type
17
+ const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
18
+ if (COMPARISON_OPERATORS.includes(op as any) || LOGICAL_OPERATORS.includes(op as any)) return 'bool' as T
19
+ if (L === R) return L
20
+ // broadcast
21
+ if (L === 'float' || L === 'int') return R
22
+ if (R === 'float' || R === 'int') return L
23
+ // mat * vec → vec
24
+ if (L === 'mat4' && R === 'vec4') return R
25
+ if (L === 'mat3' && R === 'vec3') return R
26
+ if (L === 'mat2' && R === 'vec2') return R
27
+ // vec * mat → vec
28
+ if (L === 'vec4' && R === 'mat4') return L
29
+ if (L === 'vec3' && R === 'mat3') return L
30
+ if (L === 'vec2' && R === 'mat2') return L
31
+ return L
30
32
  }
31
33
 
32
- const inferFunction = (funcName: string, args: X[], c: NodeContext): Constants => {
33
- const firstArgType = args.length > 0 ? infer(args[0], c) : 'float'
34
- if (FIRST_ARG_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
35
- if (SCALAR_RETURN_FUNCTIONS.includes(funcName as any)) return 'float'
36
- if (BOOL_RETURN_FUNCTIONS.includes(funcName as any)) return 'bool'
37
- if (PRESERVE_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
38
- if (VEC3_RETURN_FUNCTIONS.includes(funcName as any)) return 'vec3'
39
- if (VEC4_RETURN_FUNCTIONS.includes(funcName as any)) return 'vec4'
40
- if (HIGHEST_TYPE_FUNCTIONS.includes(funcName as any)) return getHighestPriorityType(args, c)
41
- return firstArgType
34
+ export const inferPrimitiveType = <T extends C>(x: X) => {
35
+ if (is.bol(x)) return 'bool' as T
36
+ if (is.str(x)) return 'texture' as T
37
+ if (is.num(x)) return 'float' as T // @TODO FIX: Number.isInteger(x) ? 'int' : 'float'
38
+ if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
39
+ return 'float' as T
42
40
  }
43
41
 
44
- const inferOperator = (leftType: string, rightType: string, op: string): Constants => {
45
- if (COMPARISON_OPERATORS.includes(op as any)) return 'bool'
46
- if (LOGICAL_OPERATORS.includes(op as any)) return 'bool'
47
- if (leftType === rightType) return leftType as Constants
48
- if (leftType.includes('vec') && !rightType.includes('vec')) return leftType as Constants
49
- if (rightType.includes('vec') && !leftType.includes('vec')) return rightType as Constants
50
- const leftPriority = CONSTANTS.indexOf(leftType as any)
51
- const rightPriority = CONSTANTS.indexOf(rightType as any)
52
- return (leftPriority >= rightPriority ? leftType : rightType) as Constants
42
+ const inferFromCount = <T extends C>(count: number) => {
43
+ return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
53
44
  }
54
45
 
55
- export const inferPrimitiveType = (x: any): Constants => {
56
- if (is.bol(x)) return 'bool'
57
- if (is.num(x)) return 'float'
58
- if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] || 'float'
59
- return 'float'
60
- }
61
-
62
- const inferSwizzle = (count: number): Constants => {
63
- return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE]!
64
- }
65
-
66
- const inferFromArray = (arr: X[], c: NodeContext): Constants => {
67
- if (arr.length === 0) return 'void'
68
- const ret = infer(arr[0], c)
46
+ const inferFromArray = <T extends C>(arr: X<T>[], c: NodeContext) => {
47
+ if (arr.length === 0) return 'void' as T
48
+ const [x] = arr
49
+ if (is.str(x)) return x as T // for struct
50
+ const ret = infer(x, c)
69
51
  for (const x of arr.slice(1))
70
- if (ret !== infer(x)) throw new Error(`glre node system error: defined scope return mismatch`)
52
+ if (ret !== infer(x, c)) throw new Error(`glre node system error: defined scope return mismatch`)
71
53
  return ret
72
54
  }
73
55
 
74
- export const inferImpl = (target: NodeProxy, c: NodeContext): Constants => {
56
+ export const inferFunction = <T extends C>(x: X) => {
57
+ return FUNCTION_RETURN_TYPES[x as keyof typeof FUNCTION_RETURN_TYPES] as T
58
+ }
59
+
60
+ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T => {
75
61
  const { type, props } = target
76
- const { id, children = [], layout, inferFrom } = props
62
+ const { id, children = [], inferFrom, layout } = props
77
63
  const [x, y, z] = children
78
- if (type === 'conversion') return x as Constants
79
- if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x as string)
80
- if (type === 'function') return inferFunction(x as string, children.slice(1), c)
81
- if (type === 'swizzle') return inferSwizzle((x as string).length)
64
+ if (type === 'conversion') return x
65
+ if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x)
82
66
  if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
83
67
  if (type === 'builtin') return inferBuiltin(id)
84
- if (type === 'define' && isConstantsType(layout?.type)) return layout?.type
68
+ if (type === 'function') return inferFunction(x) || infer(y, c)
69
+ if (type === 'define' && isConstants(layout?.type)) return layout?.type as T
70
+ if (type === 'attribute' && is.arr(x) && c.gl?.count) return inferFromCount(x.length / c.gl.count)
71
+ if (type === 'member') {
72
+ if (isSwizzle(x)) return inferFromCount(x.length)
73
+ if (isNodeProxy(y) && is.str(x)) {
74
+ const field = (y as any).props.fields?.[x] // for variable node of struct member
75
+ if (field) return infer(field, c)
76
+ }
77
+ return 'float' as T // fallback @TODO FIX
78
+ }
85
79
  if (inferFrom) return inferFromArray(inferFrom, c)
86
- return infer(x, c)
80
+ return infer(x, c) // for uniform
87
81
  }
88
82
 
89
- export const infer = (target: X, c?: NodeContext | null): Constants => {
83
+ export const infer = <T extends C>(target: X<T>, c?: NodeContext | null): T => {
90
84
  if (!c) c = {}
91
85
  if (!isNodeProxy(target)) return inferPrimitiveType(target)
92
- if (!c.infers) c.infers = new WeakMap<NodeProxy, Constants>()
93
- if (c.infers.has(target)) return c.infers.get(target)!
86
+ if (is.arr(target)) return inferFromCount(target.length)
87
+ if (!c.infers) c.infers = new WeakMap<NodeProxy<T>, C>()
88
+ if (c.infers.has(target)) return c.infers.get(target) as T
94
89
  const ret = inferImpl(target, c)
95
90
  c.infers.set(target, ret)
96
91
  return ret
package/src/node/node.ts CHANGED
@@ -1,15 +1,17 @@
1
+ import { is } from '../utils/helpers'
1
2
  import { code } from './code'
2
3
  import { assign, toVar } from './scope'
3
- import { conversionToConstant, isConversion, isFunction, isOperator, isSwizzle, getId, isNodeProxy } from './utils'
4
- import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, Swizzles, X } from './types'
4
+ import { conversionToConstant, isConversion, isFunction, isOperator, getId } from './utils'
5
+ import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, X, Constants as C } from './types'
5
6
 
6
7
  const toPrimitive = (x: X, hint: string) => {
7
8
  if (hint === 'string') return code(x)
8
9
  }
9
10
 
10
- export const node = (type: NodeTypes, props?: NodeProps | null, ...args: X[]) => {
11
+ export const node = <T extends C>(type: NodeTypes, props?: NodeProps | null, ...args: X[]) => {
11
12
  if (!props) props = {}
12
13
  if (args.length) props.children = args
14
+ const listeners = new Set<(value: any) => void>()
13
15
  const get = (_: unknown, key: string | Symbol) => {
14
16
  if (key === 'type') return type
15
17
  if (key === 'props') return props
@@ -18,33 +20,34 @@ export const node = (type: NodeTypes, props?: NodeProps | null, ...args: X[]) =>
18
20
  if (key === 'isProxy') return true
19
21
  if (key === 'toString') return code.bind(null, x)
20
22
  if (key === Symbol.toPrimitive) return toPrimitive.bind(null, x)
21
- if (isSwizzle(key)) return swizzle(key, x)
23
+ if (key === 'listeners') return listeners
22
24
  if (isOperator(key)) return (...y: X[]) => operator(key, x, ...y)
23
25
  if (isFunction(key)) return (...y: X[]) => function_(key, x, ...y)
24
26
  if (isConversion(key)) return () => conversion(conversionToConstant(key), x)
27
+ if (is.str(key)) return member(key, x) // for struct and swizzling
25
28
  }
26
29
  const set = (_: unknown, key: string, y: X) => {
27
- if (isSwizzle(key)) {
28
- swizzle(key, x).assign(y)
29
- return true
30
- }
31
- return false
30
+ if (key === 'value') listeners.forEach((fun) => fun(y))
31
+ if (is.str(key)) member(key, x).assign(y)
32
+ return true
32
33
  }
33
- const x = new Proxy({}, { get, set }) as unknown as NodeProxy
34
+ const x = new Proxy({}, { get, set }) as unknown as NodeProxy<T>
34
35
  return x
35
36
  }
36
37
 
37
- // headers
38
- export const attribute = (x: X, id = getId()) => node('attribute', { id }, x)
39
- export const constant = (x: X, id = getId()) => node('constant', { id }, x)
40
- export const uniform = (x: X, id = getId()) => node('uniform', { id }, x)
41
- export const variable = (id = getId()) => node('variable', { id })
42
- export const builtin = (id = getId()) => node('builtin', { id })
43
- export const vertexStage = (x: X, id = getId()) => node('varying', { id, inferFrom: [x] }, x)
38
+ // headers with proper type inference
39
+ export const attribute = <T extends C>(x: X, id = getId()) => node<T>('attribute', { id }, x)
40
+ export const constant = <T extends C>(x: X<T>, id = getId()) => node<T>('constant', { id }, x)
41
+ export const uniform = <T extends C>(x: X<T>, id = getId()) => node<T>('uniform', { id }, x)
42
+ export const variable = <T extends C>(id = getId()) => node<T>('variable', { id })
43
+ export const builtin = <T extends C>(id = getId()) => node<T>('builtin', { id })
44
+ export const vertexStage = <T extends C>(x: X<T>, id = getId()) => {
45
+ return node<T>('varying', { id, inferFrom: [x] }, x)
46
+ }
44
47
 
45
- // Node shorthands
46
- export const swizzle = (key: Swizzles, x: X) => node('swizzle', null, key, x)
47
- export const operator = (key: Operators, ...x: X[]) => node('operator', null, key, ...x)
48
- export const function_ = (key: Functions, ...x: X[]) => node('function', null, key, ...x)
49
- export const conversion = (key: string, ...x: X[]) => node('conversion', null, key, ...x)
50
- export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z) // x ? y : z
48
+ // Node shorthands with proper typing
49
+ export const member = <T extends C>(key: string, x: X) => node<T>('member', null, key, x)
50
+ export const select = <T extends C>(x: X<T>, y: X<T>, z: X) => node<T>('ternary', null, x, y, z) // z ? x : y @TODO REMOVE
51
+ export const operator = <T extends C>(key: Operators, ...x: X[]) => node<T>('operator', null, key, ...x)
52
+ export const function_ = <T extends C>(key: Functions, ...x: X[]) => node<T>('function', null, key, ...x)
53
+ export const conversion = <T extends C>(key: T, ...x: X[]) => node<T>('conversion', null, key, ...x)