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.
@@ -0,0 +1,160 @@
1
+ import { is } from '../utils/helpers'
2
+ import { code } from './code'
3
+ import { infer } from './infer'
4
+ import { formatConversions, isConstants, addDependency } from './utils'
5
+ import type { Constants, NodeContext, NodeProps, NodeProxy, X } from './types'
6
+
7
+ export const parseArray = (children: X[], c: NodeContext) => {
8
+ return children
9
+ .filter((x) => !is.und(x) && !is.nul(x))
10
+ .map((x) => code(x, c))
11
+ .join(', ')
12
+ }
13
+
14
+ export const parseTexture = (c: NodeContext, y: X, z: X, w: X) => {
15
+ if (c.isWebGL) {
16
+ const args = w ? [y, z, w] : [y, z]
17
+ return `texture(${parseArray(args, c)})`
18
+ }
19
+ const _y = code(y, c)
20
+ const args = [_y, _y + 'Sampler', code(z, c)]
21
+ if (!w) return `textureSample(${args})`
22
+ args.push(code(w, c))
23
+ return `textureSampleLevel(${args})`
24
+ }
25
+
26
+ /**
27
+ * scopes
28
+ */
29
+ export const parseIf = (c: NodeContext, x: X, y: X, children: X[]) => {
30
+ let ret = `if (${code(x, c)}) {\n${code(y, c)}\n}`
31
+ for (let i = 2; i < children.length; i += 2) {
32
+ const isElse = i >= children.length - 1
33
+ ret += !isElse
34
+ ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}`
35
+ : ` else {\n${code(children[i], c)}\n}`
36
+ }
37
+ return ret
38
+ }
39
+
40
+ export const parseSwitch = (c: NodeContext, x: X, children: X[]) => {
41
+ let ret = `switch (${code(x, c)}) {\n`
42
+ for (let i = 1; i < children.length; i += 2) {
43
+ const isDefault = i >= children.length - 1
44
+ if (isDefault && children.length % 2 === 0) {
45
+ ret += `default:\n${code(children[i], c)}\nbreak;\n`
46
+ } else if (i + 1 < children.length)
47
+ ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
48
+ }
49
+ ret += '}'
50
+ return ret
51
+ }
52
+
53
+ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
54
+ const type = infer(x, c)
55
+ const varName = (y as any)?.props?.id
56
+ if (c.isWebGL) return `${type} ${varName} = ${code(x, c)};`
57
+ const wgslType = formatConversions(type)
58
+ return `var ${varName}: ${wgslType} = ${code(x, c)};`
59
+ }
60
+
61
+ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants | string) => {
62
+ const { id, children = [], layout } = props
63
+ const [x, ...args] = children
64
+ const argParams: [name: string, type: string][] = []
65
+ const params: string[] = []
66
+ if (layout?.inputs)
67
+ for (const input of layout.inputs) {
68
+ argParams.push([input.name, input.type])
69
+ }
70
+ else
71
+ for (let i = 0; i < args.length; i++) {
72
+ argParams.push([`p${i}`, infer(args[i], c)])
73
+ }
74
+ const ret = []
75
+ if (c?.isWebGL) {
76
+ for (const [paramId, type] of argParams) {
77
+ addDependency(c, id!, type)
78
+ params.push(`${type} ${paramId}`)
79
+ }
80
+ addDependency(c, id!, returnType)
81
+ ret.push(`${returnType} ${id}(${params}) {`)
82
+ } else {
83
+ for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
84
+ ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
85
+ }
86
+ const scopeCode = code(x, c)
87
+ if (scopeCode) ret.push(scopeCode)
88
+ ret.push('}')
89
+ return ret.join('\n')
90
+ }
91
+
92
+ export const parseStructHead = (c: NodeContext, id: string, fields: Record<string, NodeProxy> = {}) => {
93
+ const lines: string[] = []
94
+ for (const key in fields) {
95
+ const fieldType = fields[key]
96
+ const type = infer(fieldType, c)
97
+ if (c.isWebGL) addDependency(c, id, type)
98
+ lines.push(c.isWebGL ? `${type} ${key};` : `${key}: ${formatConversions(type, c)},`)
99
+ }
100
+ const ret = lines.join('\n ')
101
+ return `struct ${id} {\n ${ret}\n};`
102
+ }
103
+
104
+ export const parseStruct = (
105
+ c: NodeContext,
106
+ id: string,
107
+ instanceId = '',
108
+ fields?: Record<string, NodeProxy>,
109
+ initialValues?: Record<string, NodeProxy>
110
+ ) => {
111
+ if (c.isWebGL) {
112
+ if (initialValues) {
113
+ const ordered = []
114
+ for (const key in fields) ordered.push(initialValues[key])
115
+ return `${id} ${instanceId} = ${id}(${parseArray(ordered, c)});`
116
+ } else return `${id} ${instanceId};`
117
+ } else {
118
+ if (initialValues) {
119
+ const ordered = []
120
+ for (const key in fields) ordered.push(initialValues[key])
121
+ return `var ${instanceId}: ${id} = ${id}(${parseArray(ordered, c)});`
122
+ } else return `var ${instanceId}: ${id};`
123
+ }
124
+ }
125
+
126
+ /**
127
+ * headers
128
+ */
129
+ export const parseVaryingHead = (c: NodeContext, id: string, type: string) => {
130
+ return c.isWebGL ? `${type} ${id};` : `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${formatConversions(type, c)}`
131
+ }
132
+
133
+ export const parseUniformHead = (c: NodeContext, id: string, type: Constants) => {
134
+ const isTexture = type === 'sampler2D' || type === 'texture'
135
+ if (c.isWebGL)
136
+ return isTexture //
137
+ ? `uniform sampler2D ${id};`
138
+ : `uniform ${type} ${id};`
139
+ if (isTexture) {
140
+ const { group = 1, binding = 0 } = c.gl?.webgpu?.textures.map.get(id) || {}
141
+ return (
142
+ `@group(${group}) @binding(${binding}) var ${id}Sampler: sampler;\n` +
143
+ `@group(${group}) @binding(${binding + 1}) var ${id}: texture_2d<f32>;`
144
+ )
145
+ }
146
+ const { group = 0, binding = 0 } = c.gl?.webgpu?.uniforms.map.get(id) || {}
147
+ const wgslType = formatConversions(type, c)
148
+ return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
149
+ }
150
+
151
+ export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
152
+ if (c.isWebGL) return `${type} ${id};`
153
+ const { location = 0 } = c.gl?.webgpu?.attribs.map.get(id) || {}
154
+ const wgslType = formatConversions(type, c)
155
+ return `@location(${location}) ${id}: ${wgslType}`
156
+ }
157
+
158
+ export const parseConstantHead = (c: NodeContext, id: string, type: Constants, value: string) => {
159
+ return c.isWebGL ? `const ${type} ${id} = ${value};` : `const ${id}: ${formatConversions(type, c)} = ${value};`
160
+ }
package/src/node/scope.ts CHANGED
@@ -3,24 +3,22 @@ import { conversion, node } from './node'
3
3
  import { getId } from './utils'
4
4
  import type { FnLayout, NodeProps, NodeProxy, X } from './types'
5
5
 
6
- let _scope: NodeProxy | null = null
7
-
8
- const scoped = (x: NodeProxy | null, fun = () => {}) => {
9
- const prev = _scope
10
- _scope = x
11
- fun()
12
- _scope = prev
13
- }
6
+ let scope: NodeProxy | null = null
7
+ let define: NodeProxy | null = null
14
8
 
15
9
  const addToScope = (x: NodeProxy) => {
16
- if (!_scope) return // ignore
17
- if (!_scope.props.children) _scope.props.children = []
18
- _scope.props.children.push(x)
10
+ if (!scope) return
11
+ if (!scope.props.children) scope.props.children = []
12
+ scope.props.children.push(x)
13
+ if (x.type !== 'return' || !define) return
14
+ const { props } = define
15
+ if (!props.inferFrom) props.inferFrom = []
16
+ props.inferFrom.push(x)
19
17
  }
20
18
 
21
19
  export const toVar = (x: X, id?: string) => {
22
20
  if (!id) id = getId()
23
- const y = node('variable', { id, inferFrom: x })
21
+ const y = node('variable', { id, inferFrom: [x] })
24
22
  const z = node('declare', null, x, y)
25
23
  addToScope(z)
26
24
  return y
@@ -32,6 +30,37 @@ export const assign = (x: X, y: X) => {
32
30
  return x
33
31
  }
34
32
 
33
+ export const Return = (x: X) => {
34
+ const y = node('return', { inferFrom: [x] }, x)
35
+ addToScope(y)
36
+ return y
37
+ }
38
+
39
+ // Struct functions
40
+ export const struct = (fields: Record<string, NodeProxy>, id = getId()) => {
41
+ return (initialValues: Record<string, NodeProxy> = {}, instanceId = getId()) => {
42
+ const x = node('variable', { id: instanceId, inferFrom: [id] })
43
+ const y = node('struct', { id, fields, initialValues }, x)
44
+ addToScope(y)
45
+ return x
46
+ }
47
+ }
48
+
49
+ const scoped = (x: NodeProxy, fun: () => NodeProxy | void, y = define) => {
50
+ // cache to revert
51
+ const _scope = scope
52
+ const _define = define
53
+ // update
54
+ scope = x
55
+ define = y
56
+ if (_scope) x.props.parent = _scope
57
+ const z = fun()
58
+ if (z) Return(z)
59
+ // revert
60
+ scope = _scope
61
+ define = _define
62
+ }
63
+
35
64
  export const If = (x: X, fun: () => void) => {
36
65
  const y = node('scope')
37
66
  scoped(y, fun)
@@ -55,7 +84,7 @@ export const If = (x: X, fun: () => void) => {
55
84
 
56
85
  export const Loop = (x: X, fun: (params: { i: NodeProxy }) => void) => {
57
86
  const y = node('scope')
58
- scoped(y, () => fun({ i: node('variable', { id: 'i', inferFrom: int(0) }) }))
87
+ scoped(y, () => fun({ i: node('variable', { id: 'i', inferFrom: [int(0)] }) }))
59
88
  const ret = node('loop', null, x, y)
60
89
  addToScope(ret)
61
90
  return ret
@@ -79,7 +108,6 @@ export const Switch = (x: X) => {
79
108
  switchNode.props.children!.push(scope)
80
109
  },
81
110
  })
82
-
83
111
  return ret()
84
112
  }
85
113
 
@@ -88,24 +116,21 @@ export const Fn = (fun: (paramVars: NodeProxy[]) => NodeProxy) => {
88
116
  const ret = (...args: X[]) => {
89
117
  const id = layout?.name || getId()
90
118
  const x = node('scope')
91
- let y: NodeProxy | undefined
92
119
  const paramVars: NodeProxy[] = []
93
120
  const paramDefs: NodeProps[] = []
94
121
  if (layout?.inputs)
95
122
  for (const input of layout.inputs) {
96
- paramDefs.push({ id: input.name, inferFrom: conversion(input.type) })
123
+ paramDefs.push({ id: input.name, inferFrom: [conversion(input.type)] })
97
124
  }
98
125
  else
99
126
  for (let i = 0; i < args.length; i++) {
100
- paramDefs.push({ id: `p${i}`, inferFrom: args[i] })
127
+ paramDefs.push({ id: `p${i}`, inferFrom: [args[i]] })
101
128
  }
102
129
  for (const props of paramDefs) paramVars.push(node('variable', props))
103
- scoped(x, () => (y = fun(paramVars)))
104
- return node('define', { id, layout }, x, y, ...args)
105
- }
106
- ret.setLayout = (newLayout: FnLayout) => {
107
- layout = newLayout
108
- return ret
130
+ const y = node('define', { id, layout }, x, ...args)
131
+ scoped(x, () => fun(paramVars), y)
132
+ return y
109
133
  }
134
+ ret.setLayout = (_layout: FnLayout) => void (layout = _layout)
110
135
  return ret
111
136
  }
package/src/node/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { GL } from '../types'
1
2
  import { CONSTANTS, CONVERSIONS, FUNCTIONS, OPERATOR_KEYS } from './const'
2
3
 
3
4
  export type Constants = (typeof CONSTANTS)[number] | 'void'
@@ -17,23 +18,68 @@ export interface FnLayout {
17
18
  }>
18
19
  }
19
20
 
21
+ /**
22
+ * Node
23
+ */
24
+ export type NodeTypes =
25
+ // headers
26
+ | 'attribute'
27
+ | 'uniform'
28
+ | 'constant'
29
+ // variables
30
+ | 'variable'
31
+ | 'varying'
32
+ | 'ternary'
33
+ | 'builtin'
34
+ | 'conversion'
35
+ | 'operator'
36
+ | 'function'
37
+ // struct
38
+ | 'struct'
39
+ | 'member'
40
+ // scopes
41
+ | 'scope'
42
+ | 'assign'
43
+ | 'loop'
44
+ | 'define'
45
+ | 'if'
46
+ | 'switch'
47
+ | 'declare'
48
+ | 'return'
49
+
20
50
  export interface NodeProps {
21
51
  id?: string
22
52
  args?: X[]
23
53
  type?: string
24
54
  children?: X[]
25
- inferFrom?: X
55
+ inferFrom?: X[]
26
56
  layout?: FnLayout
57
+ parent?: NodeProxy
58
+ // for struct
59
+ fields?: Record<string, NodeProxy>
60
+ initialValues?: Record<string, NodeProxy>
27
61
  }
28
62
 
29
- export interface NodeConfig {
63
+ export interface NodeContext {
64
+ gl?: Partial<GL>
65
+ isFrag?: boolean
30
66
  isWebGL?: boolean
31
67
  binding?: number
32
68
  infers?: WeakMap<NodeProxy, Constants>
33
- headers?: Map<string, string>
34
69
  onMount?: (name: string) => void
70
+ code?: {
71
+ headers: Map<string, string>
72
+ fragInputs: Map<string, string>
73
+ vertInputs: Map<string, string>
74
+ vertOutputs: Map<string, string>
75
+ vertVaryings: Map<string, string>
76
+ dependencies: Map<string, Set<string>>
77
+ }
35
78
  }
36
79
 
80
+ /**
81
+ * NodeProxy
82
+ */
37
83
  type _Swizzles<T extends string> = T | `${T}${T}` | `${T}${T}${T}` | `${T}${T}${T}${T}`
38
84
 
39
85
  export type Swizzles =
@@ -42,31 +88,34 @@ export type Swizzles =
42
88
  | _Swizzles<'p' | 'q'>
43
89
  | _Swizzles<'s' | 't'>
44
90
 
45
- export type NodeTypes =
46
- // headers
47
- | 'attribute'
48
- | 'uniform'
49
- | 'varying'
50
- | 'constant'
51
- // variables
52
- | 'variable'
53
- | 'swizzle'
54
- | 'ternary'
55
- | 'builtin'
56
- | 'conversion'
57
- | 'operator'
58
- | 'function'
59
- // scopes
60
- | 'scope'
91
+ type NodeProxyMethods =
92
+ | Functions
93
+ | Operators
94
+ | Conversions
95
+ | Swizzles
96
+ // system property
97
+ | 'type'
98
+ | 'props'
99
+ | 'isProxy'
61
100
  | 'assign'
62
- | 'loop'
63
- | 'define'
64
- | 'if'
65
- | 'switch'
66
- | 'declare'
101
+ | 'toVar'
102
+ | 'toString'
103
+
104
+ export type ReadNodeProxy = {
105
+ [K in string as K extends NodeProxyMethods ? never : K]: X
106
+ }
67
107
 
68
- export interface NodeProxy extends Record<Swizzles, NodeProxy> {
69
- // Operators
108
+ export interface BaseNodeProxy extends Record<Swizzles, NodeProxy> {
109
+ // System properties
110
+ assign(n: X): NodeProxy
111
+ toVar(name?: string): NodeProxy
112
+ toString(c?: NodeContext): string
113
+ type: NodeTypes
114
+ props: NodeProps
115
+ isProxy: true
116
+ listeners: Set<(value: any) => void>
117
+
118
+ // Operators methods
70
119
  add(n: X): NodeProxy
71
120
  sub(n: X): NodeProxy
72
121
  mul(n: X): NodeProxy
@@ -82,11 +131,29 @@ export interface NodeProxy extends Record<Swizzles, NodeProxy> {
82
131
  or(n: X): NodeProxy
83
132
  not(): NodeProxy
84
133
 
85
- // Variable manipulation
86
- assign(n: X): NodeProxy
87
- toVar(name?: string): NodeProxy
134
+ // Conversations methods
135
+ toBool(): NodeProxy
136
+ toUint(): NodeProxy
137
+ toInt(): NodeProxy
138
+ toFloat(): NodeProxy
139
+ toBvec2(): NodeProxy
140
+ toIvec2(): NodeProxy
141
+ toUvec2(): NodeProxy
142
+ toVec2(): NodeProxy
143
+ toBvec3(): NodeProxy
144
+ toIvec3(): NodeProxy
145
+ toUvec3(): NodeProxy
146
+ toVec3(): NodeProxy
147
+ toBvec4(): NodeProxy
148
+ toIvec4(): NodeProxy
149
+ toUvec4(): NodeProxy
150
+ toVec4(): NodeProxy
151
+ toColor(): NodeProxy
152
+ toMat2(): NodeProxy
153
+ toMat3(): NodeProxy
154
+ toMat4(): NodeProxy
88
155
 
89
- // Math function methods
156
+ // Function methods
90
157
  abs(): NodeProxy
91
158
  sin(): NodeProxy
92
159
  cos(): NodeProxy
@@ -131,32 +198,8 @@ export interface NodeProxy extends Record<Swizzles, NodeProxy> {
131
198
  dFdx(): NodeProxy
132
199
  dFdy(): NodeProxy
133
200
  fwidth(): NodeProxy
134
-
135
- // System properties
136
- toBool(): NodeProxy
137
- toUint(): NodeProxy
138
- toInt(): NodeProxy
139
- toFloat(): NodeProxy
140
- toBvec2(): NodeProxy
141
- toIvec2(): NodeProxy
142
- toUvec2(): NodeProxy
143
- toVec2(): NodeProxy
144
- toBvec3(): NodeProxy
145
- toIvec3(): NodeProxy
146
- toUvec3(): NodeProxy
147
- toVec3(): NodeProxy
148
- toBvec4(): NodeProxy
149
- toIvec4(): NodeProxy
150
- toUvec4(): NodeProxy
151
- toVec4(): NodeProxy
152
- toColor(): NodeProxy
153
- toMat2(): NodeProxy
154
- toMat3(): NodeProxy
155
- toMat4(): NodeProxy
156
- toString(c?: NodeConfig): string
157
- type: NodeTypes
158
- props: NodeProps
159
- isProxy: true
160
201
  }
161
202
 
162
- export type X = NodeProxy | number | string | boolean | undefined
203
+ export type NodeProxy = BaseNodeProxy & ReadNodeProxy
204
+
205
+ export type X = X[] | (NodeProxy | number | string | boolean | undefined)
package/src/node/utils.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import { is } from '../utils/helpers'
2
- import { code } from './code'
3
- import { infer } from './infer'
4
2
  import {
5
3
  CONSTANTS,
6
4
  CONVERSIONS,
@@ -10,17 +8,7 @@ import {
10
8
  TYPE_MAPPING,
11
9
  WGSL_TO_GLSL_BUILTIN,
12
10
  } from './const'
13
- import type {
14
- Constants,
15
- Conversions,
16
- Functions,
17
- NodeConfig,
18
- NodeProps,
19
- NodeProxy,
20
- Operators,
21
- Swizzles,
22
- X,
23
- } from './types'
11
+ import type { Constants, Conversions, Functions, NodeContext, NodeProxy, Operators, Swizzles, X } from './types'
24
12
 
25
13
  export const isSwizzle = (key: unknown): key is Swizzles => {
26
14
  return is.str(key) && /^[xyzwrgbastpq]{1,4}$/.test(key)
@@ -44,6 +32,11 @@ export const isNodeProxy = (x: unknown): x is NodeProxy => {
44
32
  return x.isProxy
45
33
  }
46
34
 
35
+ export const isConstants = (type?: unknown): type is Constants => {
36
+ if (!is.str(type)) return false
37
+ return CONSTANTS.includes(type)
38
+ }
39
+
47
40
  export const hex2rgb = (hex: number) => {
48
41
  const r = ((hex >> 16) & 0xff) / 255
49
42
  const g = ((hex >> 8) & 0xff) / 255
@@ -55,17 +48,10 @@ let count = 0
55
48
 
56
49
  export const getId = () => `i${count++}`
57
50
 
58
- export const joins = (children: X[], c: NodeConfig) => {
59
- return children
60
- .filter((x) => !is.und(x) && !is.nul(x))
61
- .map((x) => code(x, c))
62
- .join(', ')
63
- }
64
-
65
- export const formatConversions = (x: X, c?: NodeConfig) => {
51
+ export const formatConversions = (x: X, c?: NodeContext) => {
66
52
  if (!is.str(x)) return ''
67
53
  if (c?.isWebGL) return x
68
- return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING]
54
+ return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING] || x // for struct type
69
55
  }
70
56
 
71
57
  export const getOperator = (op: X) => {
@@ -81,80 +67,63 @@ export const conversionToConstant = (conversionKey: string): Constants => {
81
67
  return index !== -1 ? CONSTANTS[index] : 'float'
82
68
  }
83
69
 
84
- const generateHead = (c: NodeConfig) => {
85
- return Array.from(c.headers!)
86
- .map(([, v]) => v)
87
- .join('\n')
88
- }
89
-
90
- export const generateDefine = (props: NodeProps, c: NodeConfig) => {
91
- const { id, children = [], layout } = props
92
- const [x, y, ...args] = children
93
- const returnType = layout?.type && layout?.type !== 'auto' ? layout?.type : y ? infer(y, c) : 'void'
94
- const argParams: [name: string, type: string][] = []
95
- const params: string[] = []
96
- if (layout?.inputs)
97
- for (const input of layout.inputs) {
98
- argParams.push([input.name, input.type])
70
+ export const getEventFun = (c: NodeContext, id: string, isAttribute = false, isTexture = false) => {
71
+ if (c.isWebGL) {
72
+ if (isAttribute) return (value: any) => c.gl?.attribute?.(id, value)
73
+ if (isTexture) return (value: any) => c.gl?.texture?.(id, value)
74
+ return (value: any) => c.gl?.uniform?.(id, value)
75
+ }
76
+ if (isAttribute) return (value: any) => c.gl?._attribute?.(id, value)
77
+ if (isTexture) return (value: any) => c.gl?._texture?.(id, value)
78
+ return (value: any) => c.gl?._uniform?.(id, value)
79
+ }
80
+
81
+ export const safeEventCall = (x: X, fun: (value: unknown) => void) => {
82
+ if (!x) return
83
+ if (!isNodeProxy(x)) return fun(x) // for uniform(1)
84
+ if (x.type !== 'conversion') return
85
+ const value = x.props.children?.slice(1).filter(Boolean)
86
+ if (!value?.length) return // for uniform(vec2())
87
+ fun(value)
88
+ }
89
+
90
+ export const initNodeContext = (c: NodeContext) => {
91
+ if (!c.code) {
92
+ c.code = {
93
+ headers: new Map(),
94
+ fragInputs: new Map(),
95
+ vertInputs: new Map(),
96
+ vertOutputs: new Map(),
97
+ vertVaryings: new Map(),
98
+ dependencies: new Map(),
99
99
  }
100
- else
101
- for (let i = 0; i < args.length; i++) {
102
- argParams.push([`p${i}`, infer(args[i], c)])
100
+ if (!c.isWebGL) {
101
+ c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
102
+ c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
103
103
  }
104
- let ret = ''
105
- if (c?.isWebGL) {
106
- for (const [id, type] of argParams) params.push(`${type} ${id}`)
107
- ret += `${returnType} ${id}(${params}) {\n`
108
- } else {
109
- for (const [id, type] of argParams) params.push(`${id}: ${formatConversions(type, c)}`)
110
- ret += `fn ${id}(${params}) -> ${formatConversions(returnType, c)} {\n`
111
104
  }
112
- const scopeCode = code(x, c)
113
- if (scopeCode) ret += scopeCode + '\n'
114
- if (y) ret += `return ${code(y, c)};`
115
- ret += '\n}'
116
- return ret
117
- }
118
-
119
- const GLSL_FRAGMENT_HEAD = `
120
- #version 300 es
121
- precision mediump float;
122
- out vec4 fragColor;
123
- `.trim()
124
-
125
- const WGSL_FRAGMENT_HEAD = `
126
- @fragment
127
- fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
128
- `.trim()
129
-
130
- const generateFragmentMain = (body: string, head: string, isWebGL = true) => {
131
- let ret = ''
132
- if (isWebGL) ret += GLSL_FRAGMENT_HEAD
133
- if (head) ret += '\n' + head + '\n'
134
- if (isWebGL) ret += `void main() {\n fragColor = ${body};`
135
- else {
136
- ret += WGSL_FRAGMENT_HEAD + '\n'
137
- ret += ` return ${body};`
105
+ return c
106
+ }
107
+
108
+ export const addDependency = (c: NodeContext, id = '', type: string) => {
109
+ if (!c.code?.dependencies?.has(id)) c.code!.dependencies.set(id, new Set())
110
+ if (!isConstants(type)) c.code!.dependencies.get(id)!.add(type)
111
+ }
112
+
113
+ export const sortHeadersByDependencies = (headers: Map<string, string>, dependencies: Map<string, Set<string>>) => {
114
+ const sorted: [string, string][] = []
115
+ const visited = new Set<string>()
116
+ const visiting = new Set<string>()
117
+ const visit = (id: string) => {
118
+ if (visiting.has(id)) return
119
+ if (visited.has(id)) return
120
+ visiting.add(id)
121
+ const deps = dependencies.get(id) || new Set()
122
+ for (const dep of deps) if (headers.has(dep)) visit(dep)
123
+ visiting.delete(id)
124
+ visited.add(id)
125
+ if (headers.has(id)) sorted.push([id, headers.get(id)!])
138
126
  }
139
- ret += '\n}'
140
- return ret
141
- }
142
-
143
- const generateVertexMain = (_body: string, _head: string, isWebGL = true) => {
144
- if (isWebGL) return ``
145
- return ``
146
- }
147
-
148
- export const fragment = (x: X, c: NodeConfig = {}) => {
149
- const body = code(x, c)
150
- const head = generateHead(c)
151
- const main = generateFragmentMain(body, head, c.isWebGL)
152
- console.log(`// ↓↓↓ generated ↓↓↓\n\n${main}\n\n`)
153
- return main
154
- }
155
-
156
- export const vertex = (x: X, c: NodeConfig) => {
157
- const body = code(x, c)
158
- const head = generateHead(c)
159
- return generateVertexMain(body, head, c.isWebGL)
127
+ for (const [id] of headers) visit(id)
128
+ return sorted
160
129
  }