glre 0.37.0 → 0.38.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glre",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "author": "tseijp",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/node/node.ts CHANGED
@@ -14,7 +14,8 @@ export const node = <T extends C>(type: NodeTypes, props?: NodeProps | null, ...
14
14
  const get = (_: unknown, y: string | Symbol) => {
15
15
  if (y === 'type') return type
16
16
  if (y === 'props') return props
17
- if (y === 'toVar') return toVar.bind(null, x)
17
+ if (y === '__nodeType') return undefined // Will be inferred by TypeScript
18
+ if (y === 'toVar') return toVar.bind(null, x as any)
18
19
  if (y === 'isProxy') return true
19
20
  if (y === 'toString') return code.bind(null, x)
20
21
  if (y === Symbol.toPrimitive) return toPrimitive.bind(null, x)
@@ -43,7 +44,7 @@ export const node = <T extends C>(type: NodeTypes, props?: NodeProps | null, ...
43
44
  }
44
45
 
45
46
  // headers with proper type inference
46
- export const attribute = <T extends C>(x: X, id = getId()) => node<T>('attribute', { id }, x)
47
+ export const attribute = <T extends C>(x: X<T>, id = getId()) => node<T>('attribute', { id }, x)
47
48
  export const constant = <T extends C>(x: X<T>, id = getId()) => node<T>('constant', { id }, x)
48
49
  export const uniform = <T extends C>(x: X<T>, id = getId()) => node<T>('uniform', { id }, x)
49
50
  export const storage = <T extends C>(x: X<T>, id = getId()) => node<T>('storage', { id }, x)
package/src/node/scope.ts CHANGED
@@ -1,6 +1,17 @@
1
1
  import { getId } from './utils'
2
2
  import { conversion, node } from './node'
3
- import type { FnLayout, NodeProps, NodeProxy, X, Constants, Int } from './types'
3
+ import type {
4
+ FnLayout,
5
+ FnType,
6
+ NodeProps,
7
+ NodeProxy,
8
+ X,
9
+ Constants,
10
+ Int,
11
+ StructFactory,
12
+ StructNode,
13
+ StructFields,
14
+ } from './types'
4
15
 
5
16
  let scope: NodeProxy | null = null
6
17
  let define: NodeProxy | null = null
@@ -10,16 +21,16 @@ const addToScope = (x: NodeProxy) => {
10
21
  if (!scope.props.children) scope.props.children = []
11
22
  scope.props.children.push(x)
12
23
  if (x.type !== 'return' || !define) return
13
- // define nodes
14
24
  const { props } = define
15
25
  if (!props.inferFrom) props.inferFrom = []
16
26
  props.inferFrom.push(x)
17
27
  }
18
28
 
19
- export const toVar = <T extends Constants>(x: X<T>, id?: string): NodeProxy<T> => {
29
+ export function toVar<T extends StructFields>(x: StructNode<T>, id?: string): StructNode<T>
30
+ export function toVar<T extends Constants>(x: NodeProxy<T>, id?: string): NodeProxy<T> {
20
31
  if (!id) id = getId()
21
32
  const y = node<T>('variable', { id, inferFrom: [x] })
22
- const z = node('declare', null, x, y)
33
+ const z = node<T>('declare', null, x as NodeProxy, y)
23
34
  addToScope(z)
24
35
  return y
25
36
  }
@@ -36,12 +47,12 @@ export const Return = <T extends Constants>(x: X<T>): NodeProxy<T> => {
36
47
  return y
37
48
  }
38
49
 
39
- export const struct = <T extends Record<string, NodeProxy>>(fields: T, id = getId()) => {
40
- return (initialValues: T = {} as T, instanceId = getId()) => {
50
+ export const struct = <T extends StructFields>(fields: T, id = getId()): StructFactory<T> => {
51
+ return (initialValues: StructFields = {}, instanceId = getId()) => {
41
52
  const x = node('variable', { id: instanceId, inferFrom: [id] })
42
53
  const y = node('struct', { id, fields, initialValues }, x)
43
54
  addToScope(y)
44
- return x
55
+ return x as StructNode<T>
45
56
  }
46
57
  }
47
58
 
@@ -104,32 +115,33 @@ export const Switch = (x: NodeProxy) => {
104
115
  return ret()
105
116
  }
106
117
 
107
- export const Fn = <ReturnType extends Constants, Args extends Constants[]>(
108
- fun: (paramVars: NodeProxy[]) => NodeProxy<ReturnType> | void,
118
+ export function Fn<T extends NodeProxy | StructNode | void, Args extends any[]>(
119
+ fun: (args: Args) => T,
109
120
  defaultId = getId()
110
- ) => {
121
+ ) {
111
122
  let layout: FnLayout
112
- const ret = (...args: X<Args[number]>[]): NodeProxy<ReturnType> => {
123
+ const ret = (...args: any[]) => {
113
124
  const id = layout?.name || defaultId
114
125
  const x = node('scope')
115
126
  const paramVars: NodeProxy[] = []
116
127
  const paramDefs: NodeProps[] = []
117
- if (layout?.inputs)
128
+ if (layout?.inputs) {
118
129
  for (const input of layout.inputs) {
119
130
  paramDefs.push({ id: input.name, inferFrom: [conversion(input.type)] })
120
131
  }
121
- else
132
+ } else {
122
133
  for (let i = 0; i < args.length; i++) {
123
134
  paramDefs.push({ id: `p${i}`, inferFrom: [args[i]] })
124
135
  }
136
+ }
125
137
  for (const props of paramDefs) paramVars.push(node('variable', props))
126
- const y = node<ReturnType>('define', { id, layout }, x, ...args)
127
- scoped(x, () => fun(paramVars), y)
138
+ const y = node('define', { id, layout }, x, ...args)
139
+ scoped(x, () => fun(paramVars as Args) as any, y)
128
140
  return y
129
141
  }
130
142
  ret.setLayout = (_layout: FnLayout) => {
131
143
  layout = _layout
132
144
  return ret
133
145
  }
134
- return ret
146
+ return ret as unknown as Args extends readonly unknown[] ? FnType<T, Args> : never
135
147
  }
package/src/node/types.ts CHANGED
@@ -6,6 +6,9 @@ export type Conversions = (typeof CONVERSIONS)[number]
6
6
  export type Functions = (typeof FUNCTIONS)[number]
7
7
  export type Operators = (typeof OPERATOR_KEYS)[number]
8
8
 
9
+ /**
10
+ * scope
11
+ */
9
12
  export interface FnLayout {
10
13
  name: string
11
14
  type: Constants | 'auto'
@@ -15,8 +18,13 @@ export interface FnLayout {
15
18
  }>
16
19
  }
17
20
 
21
+ export interface FnType<T extends NodeProxy | StructNode | void, Args extends any[]> {
22
+ (...args: Args): T extends void ? Void : T
23
+ setLayout(layout: FnLayout): FnType<T, Args>
24
+ }
25
+
18
26
  /**
19
- * Node
27
+ * node
20
28
  */
21
29
  export type NodeTypes =
22
30
  // headers
@@ -48,7 +56,7 @@ export type NodeTypes =
48
56
  | 'declare'
49
57
  | 'return'
50
58
 
51
- export interface NodeProps<T extends Record<string, NodeProxy> = {}> {
59
+ export interface NodeProps {
52
60
  id?: string
53
61
  args?: any[]
54
62
  type?: string
@@ -56,8 +64,8 @@ export interface NodeProps<T extends Record<string, NodeProxy> = {}> {
56
64
  inferFrom?: any[]
57
65
  layout?: FnLayout
58
66
  // for struct
59
- fields?: T
60
- initialValues?: T
67
+ fields?: StructFields
68
+ initialValues?: StructFields
61
69
  }
62
70
 
63
71
  export interface NodeContext {
@@ -75,6 +83,7 @@ export interface NodeContext {
75
83
  vertVaryings: Map<string, string>
76
84
  computeInputs: Map<string, string>
77
85
  dependencies: Map<string, Set<string>>
86
+ structFields: Map<string, StructFields>
78
87
  }
79
88
  }
80
89
 
@@ -144,6 +153,7 @@ type NodeProxyMethods =
144
153
  | Conversions
145
154
  | Swizzles
146
155
  // system property
156
+ | '__nodeType'
147
157
  | 'type'
148
158
  | 'props'
149
159
  | 'isProxy'
@@ -159,8 +169,9 @@ type ReadNodeProxy = {
159
169
  }
160
170
 
161
171
  // Internal NodeProxy implementation (renamed from original)
162
- type NodeProxyImpl<T extends Constants = string> = BaseNodeProxy<T> & ReadNodeProxy
172
+ type NodeProxyImpl<T extends Constants> = BaseNodeProxy<T> & ReadNodeProxy
163
173
 
174
+ export type Void = NodeProxyImpl<'void'>
164
175
  export type Bool = NodeProxyImpl<'bool'>
165
176
  export type UInt = NodeProxyImpl<'uint'>
166
177
  export type Int = NodeProxyImpl<'int'>
@@ -184,8 +195,19 @@ export type Mat4 = NodeProxyImpl<'mat4'>
184
195
  export type Texture = NodeProxyImpl<'texture'>
185
196
  export type Sampler2D = NodeProxyImpl<'sampler2D'>
186
197
  export type Struct = NodeProxyImpl<'struct'>
198
+ export type StructFields = Record<string, NodeProxy>
199
+ export type StructNode<T extends StructFields = any> = Omit<Struct, keyof T> & {
200
+ [K in keyof T]: T[K] extends NodeProxy<infer U> ? NodeProxy<U> : never
201
+ } & {
202
+ toVar(id?: string): StructNode<T>
203
+ }
204
+
205
+ export interface StructFactory<T extends StructFields> {
206
+ (initialValues?: StructFields, instanceId?: string): StructNode<T>
207
+ }
187
208
 
188
209
  export interface ConstantsToType {
210
+ void: Void
189
211
  bool: Bool
190
212
  uint: UInt
191
213
  int: Int
@@ -211,14 +233,15 @@ export interface ConstantsToType {
211
233
  struct: Struct
212
234
  }
213
235
 
214
- export type NodeProxy<T extends Constants = string> = T extends keyof ConstantsToType
236
+ export type NodeProxy<T extends Constants = Constants> = T extends keyof ConstantsToType
215
237
  ? ConstantsToType[T]
216
- : NodeProxyImpl<T>
238
+ : BaseNodeProxy<T>
217
239
 
218
- export type X<T extends Constants = string> = number | string | boolean | undefined | NodeProxy<T> | X[]
240
+ export type X<T extends Constants = Constants> = number | string | boolean | undefined | NodeProxy<T>
219
241
 
220
242
  export interface BaseNodeProxy<T extends Constants> {
221
243
  // System properties
244
+ readonly __nodeType?: T
222
245
  assign(x: any): NodeProxy<T>
223
246
  toVar(name?: string): NodeProxy<T>
224
247
  toString(c?: NodeContext): string
@@ -230,6 +253,11 @@ export interface BaseNodeProxy<T extends Constants> {
230
253
  // Element access for array/matrix types
231
254
  element<Index extends X>(index: Index): NodeProxy<InferArrayElement<T>>
232
255
 
256
+ // Enhanced member access with type preservation
257
+ member<K extends string>(
258
+ key: K
259
+ ): K extends keyof T ? (T[K] extends NodeProxy<infer U> ? NodeProxy<U> : never) : never
260
+
233
261
  // Operators methods
234
262
  add<U extends Constants>(x: X<U>): NodeProxy<InferOperator<T, U>>
235
263
  sub<U extends Constants>(x: X<U>): NodeProxy<InferOperator<T, U>>
@@ -305,41 +333,41 @@ export interface BaseNodeProxy<T extends Constants> {
305
333
  * 2.1. const.ts FUNCTIONS
306
334
  */
307
335
  // 0. Component-wise functions
308
- abs(): NodeProxy
309
- acos(): NodeProxy
310
- acosh(): NodeProxy
311
- asin(): NodeProxy
312
- asinh(): NodeProxy
313
- atan(): NodeProxy
314
- atanh(): NodeProxy
315
- ceil(): NodeProxy
316
- cos(): NodeProxy
317
- cosh(): NodeProxy
318
- degrees(): NodeProxy
319
- dFdx(): NodeProxy
320
- dFdy(): NodeProxy
321
- exp(): NodeProxy
322
- exp2(): NodeProxy
323
- floor(): NodeProxy
324
- fract(): NodeProxy
325
- fwidth(): NodeProxy
326
- inverseSqrt(): NodeProxy
327
- log(): NodeProxy
328
- log2(): NodeProxy
329
- negate(): NodeProxy
330
- normalize(): NodeProxy
331
- oneMinus(): NodeProxy
332
- radians(): NodeProxy
333
- reciprocal(): NodeProxy
334
- round(): NodeProxy
335
- saturate(): NodeProxy
336
- sign(): NodeProxy
337
- sin(): NodeProxy
338
- sinh(): NodeProxy
339
- sqrt(): NodeProxy
340
- tan(): NodeProxy
341
- tanh(): NodeProxy
342
- trunc(): NodeProxy
336
+ abs(): NodeProxy<T>
337
+ acos(): NodeProxy<T>
338
+ acosh(): NodeProxy<T>
339
+ asin(): NodeProxy<T>
340
+ asinh(): NodeProxy<T>
341
+ atan(): NodeProxy<T>
342
+ atanh(): NodeProxy<T>
343
+ ceil(): NodeProxy<T>
344
+ cos(): NodeProxy<T>
345
+ cosh(): NodeProxy<T>
346
+ degrees(): NodeProxy<T>
347
+ dFdx(): NodeProxy<T>
348
+ dFdy(): NodeProxy<T>
349
+ exp(): NodeProxy<T>
350
+ exp2(): NodeProxy<T>
351
+ floor(): NodeProxy<T>
352
+ fract(): NodeProxy<T>
353
+ fwidth(): NodeProxy<T>
354
+ inverseSqrt(): NodeProxy<T>
355
+ log(): NodeProxy<T>
356
+ log2(): NodeProxy<T>
357
+ negate(): NodeProxy<T>
358
+ normalize(): NodeProxy<T>
359
+ oneMinus(): NodeProxy<T>
360
+ radians(): NodeProxy<T>
361
+ reciprocal(): NodeProxy<T>
362
+ round(): NodeProxy<T>
363
+ saturate(): NodeProxy<T>
364
+ sign(): NodeProxy<T>
365
+ sin(): NodeProxy<T>
366
+ sinh(): NodeProxy<T>
367
+ sqrt(): NodeProxy<T>
368
+ tan(): NodeProxy<T>
369
+ tanh(): NodeProxy<T>
370
+ trunc(): NodeProxy<T>
343
371
 
344
372
  // 1. Functions where first argument determines return type
345
373
  atan2<U extends Constants>(x: X<U>): NodeProxy<T>
@@ -51,9 +51,10 @@ export const TYPE_MAPPING = {
51
51
  struct: 'struct',
52
52
  } as const
53
53
 
54
- export const CONSTANTS = Object.keys(TYPE_MAPPING) as unknown as keyof typeof TYPE_MAPPING
54
+ export const CONSTANTS = Object.keys(TYPE_MAPPING) as (keyof typeof TYPE_MAPPING)[]
55
55
 
56
56
  export const OPERATORS = {
57
+ not: '', // IGNORED
57
58
  add: '+',
58
59
  sub: '-',
59
60
  mul: '*',
@@ -85,12 +85,12 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
85
85
  if (type === 'switch') return parseSwitch(c, x, children)
86
86
  if (type === 'declare') return parseDeclare(c, x, y)
87
87
  if (type === 'define') {
88
- if (!c.code?.headers.has(id)) c.code?.headers.set(id, parseDefine(c, props, infer(target, c)))
88
+ if (!c.code?.headers.has(id)) c.code?.headers.set(id, parseDefine(c, props, target))
89
89
  return `${id}(${parseArray(children.slice(1), c)})`
90
90
  }
91
91
  if (type === 'struct') {
92
92
  if (!c.code?.headers.has(id)) c.code?.headers.set(id, parseStructHead(c, id, fields))
93
- return parseStruct(c, id, x.props.id, fields, initialValues)
93
+ return parseStruct(c, id, x.props.id, initialValues)
94
94
  }
95
95
  /**
96
96
  * headers
@@ -48,8 +48,8 @@ const inferFromArray = <T extends C>(arr: X<T>[], c: NodeContext) => {
48
48
  const [x] = arr
49
49
  if (is.str(x)) return x as T // for struct
50
50
  const ret = infer(x, c)
51
- for (const x of arr.slice(1))
52
- if (ret !== infer(x, c)) throw new Error(`glre node system error: defined scope return mismatch`)
51
+ // for (const x of arr.slice(1))
52
+ // if (ret !== infer(x, c)) throw new Error(`glre node system error: defined scope return mismatch`)
53
53
  return ret
54
54
  }
55
55
 
@@ -63,7 +63,6 @@ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T
63
63
  const [x, y, z] = children
64
64
  if (type === 'conversion') return x
65
65
  if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x)
66
- if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
67
66
  if (type === 'builtin') return inferBuiltin(id)
68
67
  if (type === 'function') return inferFunction(x) || infer(y, c)
69
68
  if (type === 'define') {
@@ -75,10 +74,11 @@ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T
75
74
  if (type === 'member') {
76
75
  if (isSwizzle(y)) return inferFromCount(y.length)
77
76
  if (isNodeProxy(x)) {
78
- const field = (x as any).props.fields[y] // for variable node of struct member
79
- if (field) return infer(field, c)
77
+ const structType = infer(x, c)
78
+ const fields = c.code?.structFields?.get(structType)
79
+ if (fields && fields[y]) return infer(fields[y], c) as T
80
80
  }
81
- return 'float' as T // fallback @TODO FIX
81
+ return 'float' as T
82
82
  }
83
83
  if (inferFrom) return inferFromArray(inferFrom, c)
84
84
  return infer(x, c) // for uniform and storage gather and scatter
@@ -92,5 +92,5 @@ export const infer = <T extends C>(target: X<T>, c?: NodeContext | null): T => {
92
92
  if (c.infers.has(target)) return c.infers.get(target) as T
93
93
  const ret = inferImpl(target, c)
94
94
  c.infers.set(target, ret)
95
- return ret
95
+ return ret as T
96
96
  }
@@ -2,7 +2,7 @@ import { code } from '.'
2
2
  import { infer } from './infer'
3
3
  import { getConversions, addDependency } from './utils'
4
4
  import { is } from '../../utils/helpers'
5
- import type { Constants, NodeContext, NodeProps, NodeProxy, X } from '../types'
5
+ import type { Constants, NodeContext, NodeProps, NodeProxy, StructFields, X } from '../types'
6
6
 
7
7
  export const parseArray = (children: X[], c: NodeContext) => {
8
8
  return children
@@ -87,7 +87,8 @@ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
87
87
  return `var ${varName}: ${wgslType} = ${code(x, c)};`
88
88
  }
89
89
 
90
- export const parseStructHead = (c: NodeContext, id: string, fields: Record<string, NodeProxy> = {}) => {
90
+ export const parseStructHead = (c: NodeContext, id: string, fields: StructFields = {}) => {
91
+ c.code?.structFields?.set(id, fields)
91
92
  const lines: string[] = []
92
93
  for (const key in fields) {
93
94
  const fieldType = fields[key]
@@ -99,13 +100,8 @@ export const parseStructHead = (c: NodeContext, id: string, fields: Record<strin
99
100
  return `struct ${id} {\n ${ret}\n};`
100
101
  }
101
102
 
102
- export const parseStruct = (
103
- c: NodeContext,
104
- id: string,
105
- instanceId = '',
106
- fields?: Record<string, NodeProxy>,
107
- initialValues?: Record<string, NodeProxy>
108
- ) => {
103
+ export const parseStruct = (c: NodeContext, id: string, instanceId = '', initialValues?: StructFields) => {
104
+ const fields = c.code?.structFields?.get(id) || {}
109
105
  if (c.isWebGL) {
110
106
  if (initialValues) {
111
107
  const ordered = []
@@ -124,7 +120,7 @@ export const parseStruct = (
124
120
  /**
125
121
  * define
126
122
  */
127
- export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
123
+ export const parseDefine = (c: NodeContext, props: NodeProps, target: NodeProxy) => {
128
124
  const { id, children = [], layout } = props
129
125
  const [x, ...args] = children
130
126
  const argParams: [name: string, type: string][] = []
@@ -137,6 +133,8 @@ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Consta
137
133
  for (let i = 0; i < args.length; i++) {
138
134
  argParams.push([`p${i}`, infer(args[i], c)])
139
135
  }
136
+ const scopeCode = code(x, c) // build struct headers before inferring returnType
137
+ const returnType = infer(target, c)
140
138
  const ret = []
141
139
  if (c?.isWebGL) {
142
140
  for (const [paramId, type] of argParams) {
@@ -152,7 +150,6 @@ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Consta
152
150
  ret.push(`fn ${id}(${params}) {`)
153
151
  } else ret.push(`fn ${id}(${params}) -> ${getConversions(returnType, c)} {`)
154
152
  }
155
- const scopeCode = code(x, c)
156
153
  if (scopeCode) ret.push(scopeCode)
157
154
  ret.push('}')
158
155
  return ret.join('\n')
@@ -34,7 +34,7 @@ export const isNodeProxy = <T extends Constants>(x: unknown): x is NodeProxy<T>
34
34
 
35
35
  export const isConstants = (type?: unknown): type is Constants => {
36
36
  if (!is.str(type)) return false
37
- return CONSTANTS.includes(type)
37
+ return CONSTANTS.includes(type as any)
38
38
  }
39
39
 
40
40
  export const hex2rgb = (hex: number) => {
@@ -64,7 +64,7 @@ export const getConversions = <T extends Constants>(x: X<T>, c?: NodeContext) =>
64
64
  return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING] || x // for struct type
65
65
  }
66
66
 
67
- export const getOperator = (op: X<string>) => {
67
+ export const getOperator = (op: X) => {
68
68
  return OPERATORS[op as keyof typeof OPERATORS] || op
69
69
  }
70
70
 
@@ -86,11 +86,11 @@ export const getEventFun = (c: NodeContext, id: string, isAttribute = false, isT
86
86
 
87
87
  export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown) => void) => {
88
88
  if (is.und(x)) return
89
- if (!isNodeProxy(x)) return fun(x) // for uniform(1)
89
+ if (!isNodeProxy(x)) return fun(x) // for uniform(0) or uniform([0, 1])
90
90
  if (x.type !== 'conversion') return
91
- const value = x.props.children?.slice(1).filter(Boolean)
92
- if (!value?.length) return // for uniform(vec2())
93
- fun(value)
91
+ const args = x.props.children?.slice(1)
92
+ if (is.und(args?.[0])) return // ignore if uniform(vec2())
93
+ fun(args.map((x) => x ?? args[0])) // for uniform(vec2(1)) or uniform(vec2(1, 1))
94
94
  }
95
95
 
96
96
  export const initNodeContext = (c: NodeContext) => {
@@ -103,6 +103,7 @@ export const initNodeContext = (c: NodeContext) => {
103
103
  vertVaryings: new Map(),
104
104
  computeInputs: new Map(),
105
105
  dependencies: new Map(),
106
+ structFields: new Map(),
106
107
  }
107
108
  if (c.isWebGL) return c
108
109
  c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { EventState, Nested } from 'reev'
2
2
  import type { Fun, Queue, Frame } from 'refr'
3
- import type { NodeProxy, Vec4 } from './node'
3
+ import type { NodeProxy, Vec4, Void } from './node'
4
4
  export type { Fun, Queue, Frame }
5
5
 
6
6
  export type GL = EventState<{
@@ -21,13 +21,13 @@ export type GL = EventState<{
21
21
  particles: 64 | 256 | 576 | 1024 | 1600 | 2304 | 3136 | 4096 | 4096 | 5184 | 6400 // (8k)^2
22
22
  el: HTMLCanvasElement
23
23
  vs?: string | Vec4
24
- cs?: string | Vec4
24
+ cs?: string | Void
25
25
  fs?: string | Vec4
26
26
  vert?: string | Vec4
27
- comp?: string | Vec4
27
+ comp?: string | Void
28
28
  frag?: string | Vec4
29
29
  vertex?: string | Vec4
30
- compute?: string | Vec4
30
+ compute?: string | Void
31
31
  fragment?: string | Vec4
32
32
 
33
33
  /**
@@ -9,7 +9,7 @@ const createShader = (c: WebGLRenderingContext, source: string, type: number, on
9
9
  if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
10
10
  const error = c.getShaderInfoLog(shader)
11
11
  c.deleteShader(shader)
12
- onError(`Could not compile shader: ${error}`)
12
+ onError(`Could not compile shader: ${error}\n\n↓↓↓generated↓↓↓\n${source}`)
13
13
  }
14
14
 
15
15
  export const createProgram = (c: WebGLRenderingContext, frag: string, vert: string, gl: GL) => {