glre 0.31.0 → 0.33.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/index.ts CHANGED
@@ -5,6 +5,7 @@ import { webgpu } from './webgpu'
5
5
  import { is } from './utils/helpers'
6
6
  import type { EventState } from 'reev'
7
7
  import type { GL } from './types'
8
+ import { float, fract, int, iResolution, position, vec4, vertexIndex } from './node'
8
9
  export * from './node'
9
10
  export * from './types'
10
11
  export * from './utils/helpers'
@@ -30,6 +31,19 @@ export const isWebGPUSupported = () => {
30
31
 
31
32
  let iTime = performance.now()
32
33
 
34
+ const defaultFragment = () => vec4(fract(position.xy.div(iResolution)), 0, 1)
35
+ const defaultVertex = () =>
36
+ vec4(
37
+ float(int(vertexIndex).mod(int(2)))
38
+ .mul(4)
39
+ .sub(1),
40
+ float(int(vertexIndex).div(int(2)))
41
+ .mul(4)
42
+ .sub(1),
43
+ 0,
44
+ 1
45
+ )
46
+
33
47
  export const createGL = (props?: Partial<GL>) => {
34
48
  const gl = event<Partial<GL>>({
35
49
  isNative: false,
@@ -46,14 +60,14 @@ export const createGL = (props?: Partial<GL>) => {
46
60
  gl.queue = createQueue()
47
61
  gl.frame = createFrame()
48
62
 
49
- gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)))
50
- gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)))
51
- gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)))
63
+ gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)), gl)
64
+ gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)), gl)
65
+ gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)), gl)
52
66
  gl.uniform({ iResolution: gl.size, iMouse: [0, 0], iTime })
53
67
 
54
68
  gl('mount', async () => {
55
- gl.vs = gl.vs || gl.vert || gl.vertex
56
- gl.fs = gl.fs || gl.frag || gl.fragment
69
+ gl.vs = gl.vs || gl.vert || gl.vertex || defaultVertex()
70
+ gl.fs = gl.fs || gl.frag || gl.fragment || defaultFragment()
57
71
  if (!isWebGPUSupported()) gl.isWebGL = true
58
72
  if (gl.isWebGL) {
59
73
  gl((await webgl(gl)) as GL)
package/src/node/code.ts CHANGED
@@ -1,11 +1,26 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import { infer } from './infer'
3
- import { getBluiltin, getOperator, generateDefine, formatConversions, joins } from './utils'
4
- import type { NodeConfig, X } from './types'
3
+ import {
4
+ parseArray,
5
+ parseAttribHead,
6
+ parseConstantHead,
7
+ parseDeclare,
8
+ parseDefine,
9
+ parseIf,
10
+ parseStruct,
11
+ parseStructHead,
12
+ parseSwitch,
13
+ parseTexture,
14
+ parseVaryingHead,
15
+ parseUniformHead,
16
+ } from './parse'
17
+ import { getBluiltin, getOperator, formatConversions, safeEventCall, getEventFun, initNodeContext } from './utils'
18
+ import type { NodeContext, NodeProxy, X } from './types'
5
19
 
6
- export const code = (target: X, c?: NodeConfig | null): string => {
20
+ export const code = (target: X, c?: NodeContext | null): string => {
7
21
  if (!c) c = {}
8
- if (!c.headers) c.headers = new Map()
22
+ initNodeContext(c)
23
+ if (is.arr(target)) return parseArray(target, c)
9
24
  if (is.str(target)) return target
10
25
  if (is.num(target)) {
11
26
  const ret = `${target}`
@@ -13,103 +28,90 @@ export const code = (target: X, c?: NodeConfig | null): string => {
13
28
  return ret + '.0'
14
29
  }
15
30
  if (is.bol(target)) return target ? 'true' : 'false'
16
- if (!target) return '' // ignore if no target
31
+ if (!target) return ''
17
32
  const { type, props } = target
18
- const { id = '', children = [] } = props
19
- const [x, y, z] = children
20
- /**
21
- * headers
22
- */
23
- let head = ''
24
- if (type === 'attribute') {
25
- if (c.headers.has(id)) return id
26
- head = `${infer(target, c)} ${id}`
27
- }
28
- if (type === 'uniform') {
29
- if (c.headers.has(id)) return id
30
- if (!c.binding) c.binding = 0
31
- const varType = infer(target, c)
32
- head = c.isWebGL
33
- ? `uniform ${varType} ${id};`
34
- : `@group(0) @binding(${c.binding++}) var<uniform> ${id}: ${formatConversions(varType, c)};`
35
- }
36
- if (type === 'constant') {
37
- if (c.headers.has(id)) return id
38
- const varType = infer(target, c)
39
- const value = code(x, c)
40
- head = c.isWebGL
41
- ? `const ${varType} ${id} = ${value};`
42
- : `const ${id}: ${formatConversions(varType, c)} = ${value};`
43
- }
44
- if (type === 'varying') {
45
- if (c.headers.has(id)) return id
46
- head = `${infer(target, c)} ${id}`
47
- }
48
- if (head) {
49
- c.headers.set(id, head)
50
- c.onMount?.(id)
51
- return id
52
- }
33
+ const { id = '', children = [], fields, initialValues } = props
34
+ const [x, y, z, w] = children
53
35
  /**
54
36
  * variables
55
37
  */
56
38
  if (type === 'variable') return id
57
- if (type === 'swizzle') return `${code(y, c)}.${code(x, c)}`
58
- if (type === 'ternary') return `(${code(x, c)} ? ${code(y, c)} : ${code(z, c)})`
59
- if (type === 'builtin') return c?.isWebGL ? getBluiltin(id) : id
60
- if (type === 'conversion') return `${formatConversions(x, c)}(${joins(children.slice(1), c)})`
39
+ if (type === 'member') return `${code(y, c)}.${code(x, c)}`
40
+ if (type === 'ternary')
41
+ return c.isWebGL
42
+ ? `(${code(x, c)} ? ${code(y, c)} : ${code(z, c)})`
43
+ : `select(${code(z, c)}, ${code(y, c)}, ${code(x, c)})`
44
+ if (type === 'conversion') return `${formatConversions(x, c)}(${parseArray(children.slice(1), c)})`
61
45
  if (type === 'operator') {
62
46
  if (x === 'not' || x === 'bitNot') return `!${code(y, c)}`
63
47
  return `(${code(y, c)} ${getOperator(x)} ${code(z, c)})`
64
48
  }
65
49
  if (type === 'function') {
66
- if (x === 'negate') return `(-${joins(children.slice(1), c)})`
67
- return `${x}(${joins(children.slice(1), c)})`
50
+ if (x === 'negate') return `(-${parseArray(children.slice(1), c)})`
51
+ if (x === 'texture') return parseTexture(c, y, z, w)
52
+ return `${x}(${parseArray(children.slice(1), c)})`
68
53
  }
69
54
  /**
70
55
  * scopes
71
56
  */
72
57
  if (type === 'scope') return children.map((child: any) => code(child, c)).join('\n')
73
58
  if (type === 'assign') return `${code(x, c)} = ${code(y, c)};`
59
+ if (type === 'return') return `return ${code(x, c)};`
74
60
  if (type === 'loop')
75
61
  return c.isWebGL
76
62
  ? `for (int i = 0; i < ${x}; i += 1) {\n${code(y, c)}\n}`
77
63
  : `for (var i: i32 = 0; i < ${x}; i++) {\n${code(y, c)}\n}`
64
+ if (type === 'if') return parseIf(c, x, y, children)
65
+ if (type === 'switch') return parseSwitch(c, x, children)
66
+ if (type === 'declare') return parseDeclare(c, x, y)
78
67
  if (type === 'define') {
79
- const args = children.slice(2)
80
- const ret = `${id}(${args.map((arg) => code(arg, c))})`
81
- if (c.headers.has(id)) return ret
82
- c.headers.set(id, generateDefine(props, c))
83
- return ret
68
+ if (!c.code?.headers.has(id)) c.code?.headers.set(id, parseDefine(c, props, infer(target, c)))
69
+ return `${id}(${parseArray(children.slice(1), c)})`
84
70
  }
85
- if (type === 'if') {
86
- let ret = `if (${code(x, c)}) {\n${code(y, c)}\n}`
87
- for (let i = 2; i < children.length; i += 2) {
88
- const isElseIf = i >= children.length - 1
89
- ret += !isElseIf
90
- ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}`
91
- : ` else {\n${code(children[i], c)}\n}`
92
- }
93
- return ret
71
+ if (type === 'struct') {
72
+ if (!c.code?.headers.has(id)) c.code?.headers.set(id, parseStructHead(c, id, fields))
73
+ return parseStruct(c, id, (x as NodeProxy).props.id, fields, initialValues)
74
+ }
75
+ /**
76
+ * headers
77
+ */
78
+ if (type === 'varying') {
79
+ if (c.code?.vertOutputs.has(id)) return c.isWebGL ? `${id}` : `out.${id}`
80
+ const field = parseVaryingHead(c, id, infer(target, c))
81
+ c.code?.fragInputs.set(id, field)
82
+ c.code?.vertOutputs.set(id, field)
83
+ c.code?.vertVaryings.set(id, code(x, c))
84
+ return c.isWebGL ? `${id}` : `out.${id}`
94
85
  }
95
- if (type === 'switch') {
96
- let ret = `switch (${code(x, c)}) {\n`
97
- for (let i = 1; i < children.length; i += 2) {
98
- const isDefault = i >= children.length - 1
99
- if (isDefault && children.length % 2 === 0) {
100
- ret += `default:\n${code(children[i], c)}\nbreak;\n`
101
- } else if (i + 1 < children.length)
102
- ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
103
- }
104
- ret += '}'
105
- return ret
86
+ if (type === 'builtin') {
87
+ if (c.isWebGL) return getBluiltin(id)
88
+ if (id === 'position') return 'out.position'
89
+ const field = `@builtin(${id}) ${id}: ${formatConversions(infer(target, c), c)}`
90
+ if (c.isFrag) {
91
+ c.code?.fragInputs.set(id, field)
92
+ } else c.code?.vertInputs.set(id, field)
93
+ return `in.${id}`
106
94
  }
107
- if (type === 'declare') {
108
- const varType = infer(x, c)
109
- const varName = (y as any)?.props?.id
110
- if (c.isWebGL) return `${varType} ${varName} = ${code(x, c)};`
111
- const wgslType = formatConversions(varType)
112
- return `var ${varName}: ${wgslType} = ${code(x, c)};`
95
+ if (type === 'attribute') {
96
+ const fun = getEventFun(c, id, true)
97
+ safeEventCall(x, fun)
98
+ target.listeners.add(fun)
99
+ c.code?.vertInputs.set(id, parseAttribHead(c, id, infer(target, c)))
100
+ return c.isWebGL ? `${id}` : `in.${id}`
101
+ }
102
+ if (c.code?.headers.has(id)) return id // must last
103
+ let head = ''
104
+ if (type === 'uniform') {
105
+ const varType = infer(target, c)
106
+ const fun = getEventFun(c, id, false, varType === 'texture')
107
+ safeEventCall(x, fun)
108
+ target.listeners.add(fun)
109
+ head = parseUniformHead(c, id, varType)
110
+ }
111
+ if (type === 'constant') head = parseConstantHead(c, id, infer(target, c), code(x, c))
112
+ if (head) {
113
+ c.code?.headers.set(id, head)
114
+ return id
113
115
  }
114
116
  return code(x, c)
115
117
  }
package/src/node/const.ts CHANGED
@@ -1,27 +1,32 @@
1
1
  export const SWIZZLES = ['x', 'y', 'z', 'w', 'r', 'g', 'b', 'a', 's', 't', 'p', 'q'] as const
2
2
 
3
- export const CONSTANTS = [
4
- 'bool',
5
- 'uint',
6
- 'int',
7
- 'float',
8
- 'bvec2',
9
- 'ivec2',
10
- 'uvec2',
11
- 'vec2',
12
- 'bvec3',
13
- 'ivec3',
14
- 'uvec3',
15
- 'vec3',
16
- 'bvec4',
17
- 'ivec4',
18
- 'uvec4',
19
- 'vec4',
20
- 'color',
21
- 'mat2',
22
- 'mat3',
23
- 'mat4',
24
- ] as const
3
+ export const TYPE_MAPPING = {
4
+ bool: 'bool',
5
+ uint: 'u32',
6
+ int: 'i32',
7
+ float: 'f32',
8
+ bvec2: 'vec2<bool>',
9
+ ivec2: 'vec2i',
10
+ uvec2: 'vec2u',
11
+ vec2: 'vec2f',
12
+ bvec3: 'vec3<bool>',
13
+ ivec3: 'vec3i',
14
+ uvec3: 'vec3u',
15
+ vec3: 'vec3f',
16
+ bvec4: 'vec4<bool>',
17
+ ivec4: 'vec4i',
18
+ uvec4: 'vec4u',
19
+ vec4: 'vec4f',
20
+ color: 'color',
21
+ mat2: 'mat2x2f',
22
+ mat3: 'mat3x3f',
23
+ mat4: 'mat4x4f',
24
+ texture: 'texture_2d<f32>',
25
+ sampler2D: 'sampler',
26
+ struct: 'struct',
27
+ } as const
28
+
29
+ export const CONSTANTS = Object.keys(TYPE_MAPPING) as unknown as keyof typeof TYPE_MAPPING
25
30
 
26
31
  export const CONVERSIONS = [
27
32
  'toBool',
@@ -138,28 +143,6 @@ export const FUNCTIONS = [
138
143
  ...ADDITIONAL_FUNCTIONS,
139
144
  ] as const
140
145
 
141
- export const TYPE_MAPPING = {
142
- float: 'f32',
143
- int: 'i32',
144
- uint: 'u32',
145
- bool: 'bool',
146
- vec2: 'vec2f',
147
- vec3: 'vec3f',
148
- vec4: 'vec4f',
149
- mat2: 'mat2x2f',
150
- mat3: 'mat3x3f',
151
- mat4: 'mat4x4f',
152
- ivec2: 'vec2i',
153
- ivec3: 'vec3i',
154
- ivec4: 'vec4i',
155
- uvec2: 'vec2u',
156
- uvec3: 'vec3u',
157
- uvec4: 'vec4u',
158
- bvec2: 'vec2<bool>',
159
- bvec3: 'vec3<bool>',
160
- bvec4: 'vec4<bool>',
161
- } as const
162
-
163
146
  export const COMPONENT_COUNT_TO_TYPE = {
164
147
  1: 'float',
165
148
  2: 'vec2',
package/src/node/index.ts CHANGED
@@ -1,15 +1,88 @@
1
- import { builtin, conversion as c, function_ as f, uniform } from './node'
2
- import { hex2rgb } from './utils'
3
1
  import { is } from '../utils/helpers'
4
- import type { X } from './types'
2
+ import { code } from './code'
3
+ import { builtin, conversion as c, function_ as f, uniform as u } from './node'
4
+ import { hex2rgb, sortHeadersByDependencies } from './utils'
5
+ import type { NodeContext, X } from './types'
5
6
  export * from './code'
6
- export * from './const'
7
- export * from './infer'
8
7
  export * from './node'
9
8
  export * from './scope'
10
9
  export * from './types'
11
10
  export * from './utils'
12
11
 
12
+ const GLSL_FRAGMENT_HEAD = `
13
+ #version 300 es
14
+ precision mediump float;
15
+ out vec4 fragColor;
16
+ `.trim()
17
+
18
+ const generateHead = (x: X, c: NodeContext) => {
19
+ const body = code(x, c)
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')
25
+ return [head, body]
26
+ }
27
+
28
+ const generateStruct = (id: string, map: Map<string, string>) => {
29
+ return `struct ${id} {\n ${Array.from(map.values()).join(',\n ')}\n}`
30
+ }
31
+
32
+ export const vertex = (x: X, c: NodeContext) => {
33
+ if (is.str(x)) return x.trim()
34
+ c.code?.headers?.clear()
35
+ c.isFrag = false // for varying inputs or outputs
36
+ const [head, body] = generateHead(x, c)
37
+ const ret = []
38
+ if (c.isWebGL) {
39
+ ret.push('#version 300 es')
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}`)
42
+ ret.push(head)
43
+ ret.push('void main() {')
44
+ ret.push(` gl_Position = ${body};`)
45
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` ${id} = ${code};`)
46
+ } else {
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))
49
+ ret.push(head)
50
+ ret.push('@vertex')
51
+ ret.push(`fn main(${c.code?.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
52
+ ret.push(' var out: Out;')
53
+ ret.push(` out.position = ${body};`)
54
+ for (const [id, code] of c.code?.vertVaryings?.entries() || []) ret.push(` out.${id} = ${code};`)
55
+ ret.push(' return out;')
56
+ }
57
+ ret.push('}')
58
+ const main = ret.filter(Boolean).join('\n').trim()
59
+ console.log(`↓↓↓generated↓↓↓\n${main}`)
60
+ return main
61
+ }
62
+
63
+ export const fragment = (x: X, c: NodeContext) => {
64
+ if (is.str(x)) return x.trim()
65
+ c.code?.headers?.clear()
66
+ c.isFrag = true // for varying inputs or outputs
67
+ const [head, body] = generateHead(x, c)
68
+ const ret = []
69
+ if (c.isWebGL) {
70
+ ret.push(GLSL_FRAGMENT_HEAD)
71
+ for (const code of c.code?.fragInputs?.values() || []) ret.push(`in ${code}`)
72
+ ret.push(head)
73
+ ret.push(`void main() {\n fragColor = ${body};`)
74
+ } else {
75
+ if (c.code?.fragInputs?.size) ret.push(generateStruct('Out', c.code.fragInputs))
76
+ ret.push(head)
77
+ ret.push(`@fragment\nfn main(out: Out) -> @location(0) vec4f {`)
78
+ ret.push(` return ${body};`)
79
+ }
80
+ ret.push('}')
81
+ const main = ret.filter(Boolean).join('\n').trim()
82
+ console.log(`↓↓↓generated↓↓↓\n${main}`)
83
+ return main
84
+ }
85
+
13
86
  // Builtin Variables
14
87
  export const position = builtin('position')
15
88
  export const vertexIndex = builtin('vertex_index')
@@ -31,10 +104,10 @@ export const screenCoordinate = builtin('screenCoordinate')
31
104
  export const screenUV = builtin('screenUV')
32
105
 
33
106
  // Type constructors
34
- export const float = (x: X) => c('float', x)
35
- export const int = (x: X) => c('int', x)
36
- export const uint = (x: X) => c('uint', x)
37
- export const bool = (x: X) => c('bool', x)
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)
38
111
  export const vec2 = (x?: X, y?: X) => c('vec2', x, y)
39
112
  export const vec3 = (x?: X, y?: X, z?: X) => c('vec3', x, y, z)
40
113
  export const vec4 = (x?: X, y?: X, z?: X, w?: X) => c('vec4', x, y, z, w)
@@ -50,15 +123,18 @@ export const uvec4 = (x?: X, y?: X, z?: X, w?: X) => c('uvec4', x, y, z, w)
50
123
  export const bvec2 = (x?: X, y?: X) => c('bvec2', x, y)
51
124
  export const bvec3 = (x?: X, y?: X, z?: X) => c('bvec3', x, y, z)
52
125
  export const bvec4 = (x?: X, y?: X, z?: X, w?: X) => c('bvec4', x, y, z, w)
126
+ export const texture2D = (x?: X) => c('texture', x)
127
+ export const sampler2D = () => c('sampler2D')
53
128
  export const color = (r?: X, g?: X, b?: X) => {
54
129
  if (is.num(r) && is.und(g) && is.und(b)) return vec3(...hex2rgb(r))
55
130
  return vec3(r, g, b)
56
131
  }
57
132
 
58
133
  // Default uniforms
59
- export const iResolution = uniform(vec2(1280, 800), 'iResolution')
60
- export const iMouse = uniform(vec2(0, 0), 'iMouse')
61
- export const iTime = uniform(float(0), 'iTime')
134
+ export const iResolution = u(vec2(), 'iResolution')
135
+ export const iMouse = u(vec2(), 'iMouse')
136
+ export const iTime = u(float(), 'iTime')
137
+ export const uv = () => position.xy.div(iResolution)
62
138
 
63
139
  // Texture Functions
64
140
  export const texture = (x: X, y: X, z?: X) => f('texture', x, y, z)
@@ -121,3 +197,13 @@ export const step = (edge: X, x: X) => f('step', edge, x)
121
197
  export const tan = (x: X) => f('tan', x)
122
198
  export const transformDirection = (dir: X, matrix: X) => f('transformDirection', dir, matrix)
123
199
  export const trunc = (x: X) => f('trunc', x)
200
+
201
+ // // Struct functions
202
+ // export const struct = (fields: Record<string, X>) => {
203
+ // const id = getId()
204
+ // const structNode = node('struct', { id, fields })
205
+ // // Create constructor function
206
+ // const constructor = () => node('struct', { id: getId(), type: id })
207
+ // Object.assign(constructor, structNode)
208
+ // return constructor as any
209
+ // }
package/src/node/infer.ts CHANGED
@@ -13,10 +13,10 @@ import {
13
13
  VEC3_RETURN_FUNCTIONS,
14
14
  VEC4_RETURN_FUNCTIONS,
15
15
  } from './const'
16
- import { isNodeProxy } from './utils'
17
- import type { Constants, NodeConfig, NodeProxy, X } from './types'
16
+ import { isConstants, isNodeProxy, isSwizzle } from './utils'
17
+ import type { Constants, NodeContext, NodeProxy, X } from './types'
18
18
 
19
- const getHighestPriorityType = (args: X[], c: NodeConfig) => {
19
+ const getHighestPriorityType = (args: X[], c: NodeContext) => {
20
20
  return args.reduce((highest, current) => {
21
21
  const currentType = infer(current, c)
22
22
  const highestPriority = CONSTANTS.indexOf(highest as any)
@@ -29,7 +29,7 @@ const inferBuiltin = (id: string | undefined): Constants => {
29
29
  return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES]!
30
30
  }
31
31
 
32
- const inferFunction = (funcName: string, args: X[], c: NodeConfig): Constants => {
32
+ const inferFunction = (funcName: string, args: X[], c: NodeContext): Constants => {
33
33
  const firstArgType = args.length > 0 ? infer(args[0], c) : 'float'
34
34
  if (FIRST_ARG_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
35
35
  if (SCALAR_RETURN_FUNCTIONS.includes(funcName as any)) return 'float'
@@ -52,38 +52,55 @@ const inferOperator = (leftType: string, rightType: string, op: string): Constan
52
52
  return (leftPriority >= rightPriority ? leftType : rightType) as Constants
53
53
  }
54
54
 
55
- const inferPrimitiveType = (x: any): Constants => {
55
+ export const inferPrimitiveType = (x: any): Constants => {
56
56
  if (is.bol(x)) return 'bool'
57
- if (is.num(x)) return 'float' // Number.isInteger(x) ? 'int' : 'float' // @TODO FIX
57
+ if (is.str(x)) return 'texture'
58
+ if (is.num(x)) return Number.isInteger(x) ? 'int' : 'float'
58
59
  if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] || 'float'
59
60
  return 'float'
60
61
  }
61
62
 
62
- const inferSwizzle = (count: number): Constants => {
63
+ const inferFromCount = (count: number): Constants => {
63
64
  return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE]!
64
65
  }
65
66
 
66
- export const inferImpl = (target: NodeProxy, c: NodeConfig): Constants => {
67
+ const inferFromArray = (arr: X[], c: NodeContext): Constants => {
68
+ if (arr.length === 0) return 'void'
69
+ const [x] = arr
70
+ if (is.str(x)) return x as Constants // for struct
71
+ const ret = infer(x, c)
72
+ for (const x of arr.slice(1))
73
+ if (ret !== infer(x, c)) throw new Error(`glre node system error: defined scope return mismatch`)
74
+ return ret
75
+ }
76
+
77
+ export const inferImpl = (target: NodeProxy, c: NodeContext): Constants => {
67
78
  const { type, props } = target
68
- const { id, children = [], inferFrom } = props
79
+ const { id, children = [], layout, inferFrom } = props
69
80
  const [x, y, z] = children
70
- if (inferFrom) return infer(inferFrom, c)
71
81
  if (type === 'conversion') return x as Constants
72
82
  if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x as string)
73
83
  if (type === 'function') return inferFunction(x as string, children.slice(1), c)
74
- if (type === 'swizzle') return inferSwizzle((x as string).length)
75
84
  if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
76
85
  if (type === 'builtin') return inferBuiltin(id)
77
- if (type === 'define') {
78
- if (props.layout?.type && props.layout?.type !== 'auto') return props.layout.type as Constants
79
- return y ? infer(y, c) : 'void'
86
+ if (type === 'define' && isConstants(layout?.type)) return layout?.type
87
+ if (type === 'attribute' && is.arr(x) && c.gl?.count) return inferFromCount(x.length / c.gl.count)
88
+ if (type === 'member') {
89
+ if (isSwizzle(x)) return inferFromCount(x.length)
90
+ if (isNodeProxy(y) && is.str(x)) {
91
+ const field = y.props.fields?.[x] // for variable node of struct member
92
+ if (field) return infer(field, c)
93
+ }
94
+ return 'float' // fallback @TODO FIX
80
95
  }
96
+ if (inferFrom) return inferFromArray(inferFrom, c)
81
97
  return infer(x, c)
82
98
  }
83
99
 
84
- export const infer = (target: X, c?: NodeConfig | null): Constants => {
100
+ export const infer = (target: X, c?: NodeContext | null): Constants => {
85
101
  if (!c) c = {}
86
102
  if (!isNodeProxy(target)) return inferPrimitiveType(target)
103
+ if (is.arr(target)) return inferFromCount(target.length)
87
104
  if (!c.infers) c.infers = new WeakMap<NodeProxy, Constants>()
88
105
  if (c.infers.has(target)) return c.infers.get(target)!
89
106
  const ret = inferImpl(target, c)
package/src/node/node.ts CHANGED
@@ -1,7 +1,8 @@
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 } 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 } from './types'
5
6
 
6
7
  const toPrimitive = (x: X, hint: string) => {
7
8
  if (hint === 'string') return code(x)
@@ -10,6 +11,7 @@ const toPrimitive = (x: X, hint: string) => {
10
11
  export const node = (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,35 +20,32 @@ 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
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
34
  const x = new Proxy({}, { get, set }) as unknown as NodeProxy
34
35
  return x
35
36
  }
36
37
 
37
38
  // headers
38
- export const attribute = (x: X, id?: string) => node('varying', { id }, x)
39
- export const uniform = (x: X, id?: string) => node('uniform', { id }, x)
40
- export const varying = (x: X, id?: string) => node('varying', { id }, x)
41
- export const constant = (x: X, id?: string) => node('constant', { id }, x)
42
- export const variable = (id: string) => node('variable', { id })
43
- export const builtin = (id: string) => node('builtin', { id })
39
+ export const attribute = (x: X, id = getId()) => node('attribute', { id }, x)
40
+ export const constant = (x: X, id = getId()) => node('constant', { id }, x)
41
+ export const uniform = (x: X, id = getId()) => node('uniform', { id }, x)
42
+ export const variable = (id = getId()) => node('variable', { id })
43
+ export const builtin = (id = getId()) => node('builtin', { id })
44
+ export const vertexStage = (x: X, id = getId()) => node('varying', { id, inferFrom: [x] }, x)
44
45
 
45
46
  // Node shorthands
46
- export const swizzle = (key: Swizzles, arg: X) => node('swizzle', null, key, arg)
47
- export const operator = (key: Operators, ...args: X[]) => node('operator', null, key, ...args)
48
- export const function_ = (key: Functions, ...args: X[]) => node('function', null, key, ...args)
49
- export const conversion = (key: string, ...args: X[]) => node('conversion', null, key, ...args)
50
-
51
- // x ? y : z
52
- export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z)
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 member = (key: string, x: X) => node('member', null, key, x)
51
+ export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z) // x ? y : z @TODO REMOVE