kmcom-nuxt-layers 2.2.12 → 2.2.13

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.
Files changed (98) hide show
  1. package/docs/FEEDS.md +1 -2
  2. package/layers/animations/app/composables/useMagneticElement.ts +11 -9
  3. package/layers/animations/app/composables/useTiltEffect.ts +11 -9
  4. package/layers/animations/app/utils/pointerMotion.ts +31 -0
  5. package/layers/canvas/app/components/ShaderCanvas.vue +2 -2
  6. package/layers/content/app/composables/useCollectionItems.ts +28 -0
  7. package/layers/content/app/composables/useGalleryItems.ts +8 -14
  8. package/layers/content/app/composables/usePortfolioItems.ts +10 -18
  9. package/layers/core/app/composables/useBrowser.ts +9 -82
  10. package/layers/core/app/composables/useFeatures.ts +3 -27
  11. package/layers/core/app/plugins/init.ts +157 -135
  12. package/layers/core/app/utils/browserInfo.ts +115 -0
  13. package/layers/core/app/utils/featureClasses.ts +40 -0
  14. package/layers/core/app/utils/helpers.test.ts +51 -0
  15. package/layers/feeds/app/components/Feeds/Index.vue +1 -1
  16. package/layers/feeds/app/components/Feeds/RouteCard.vue +3 -9
  17. package/layers/feeds/app/utils/feed-catalog.ts +9 -4
  18. package/layers/feeds/nuxt.config.ts +0 -1
  19. package/layers/feeds/server/utils/content-adapter.test.ts +68 -0
  20. package/layers/feeds/server/utils/content-adapter.ts +2 -22
  21. package/layers/feeds/server/utils/feed-author.ts +32 -0
  22. package/layers/feeds/server/utils/feed-config.ts +88 -0
  23. package/layers/feeds/server/utils/feed-service.ts +11 -30
  24. package/layers/feeds/server/utils/feed-xml.ts +26 -0
  25. package/layers/feeds/server/utils/formats/rss.ts +10 -15
  26. package/layers/feeds/server/utils/formats.test.ts +71 -0
  27. package/layers/forms/app/components/Form/Field.vue +42 -30
  28. package/layers/forms/app/utils/fieldProps.ts +65 -0
  29. package/layers/layout/app/components/Layout/Grid/Item.vue +29 -146
  30. package/layers/layout/app/utils/gridPlacementStyle.ts +195 -0
  31. package/layers/mailer/app/types/mailer.ts +7 -25
  32. package/layers/mailer/server/utils/email.ts +28 -13
  33. package/layers/mailer/server/utils/hooks.ts +1 -20
  34. package/layers/navigation/app/composables/useSite.ts +2 -9
  35. package/layers/navigation/app/utils/site.ts +26 -0
  36. package/layers/routing/app/utils/resolveRoute.test.ts +47 -0
  37. package/layers/routing/app/utils/resolveRoute.ts +19 -10
  38. package/layers/scripts/app/composables/useAnalytics.ts +8 -41
  39. package/layers/scripts/app/composables/useGtm.ts +6 -13
  40. package/layers/scripts/app/utils/scriptClients.ts +70 -0
  41. package/layers/scroll/app/composables/useSmoothScroll.ts +9 -43
  42. package/layers/scroll/app/utils/scroll.ts +103 -0
  43. package/layers/seo/app/composables/useSeoConfig.ts +3 -9
  44. package/layers/seo/app/utils/seoConfig.ts +38 -0
  45. package/layers/shader/app/components/Material/AmbientAurora.client.vue +11 -33
  46. package/layers/shader/app/components/Material/AmbientFlow.client.vue +10 -37
  47. package/layers/shader/app/components/Material/AmbientGradientMesh.client.vue +10 -37
  48. package/layers/shader/app/components/Material/AmbientNebula.client.vue +12 -37
  49. package/layers/shader/app/components/Material/AmbientOcean.client.vue +9 -33
  50. package/layers/shader/app/components/Material/Gradient.client.vue +25 -46
  51. package/layers/shader/app/components/Material/Image.client.vue +10 -55
  52. package/layers/shader/app/components/Material/Node.client.vue +18 -5
  53. package/layers/shader/app/components/Material/Noise.client.vue +9 -43
  54. package/layers/shader/app/components/Preset/ThemeBubble.client.vue +2 -1
  55. package/layers/shader/app/components/Preset/ThemeFlow.client.vue +2 -1
  56. package/layers/shader/app/components/Preset/ThemeGradient.client.vue +2 -1
  57. package/layers/shader/app/components/Preset/ThemeLavaLamp.client.vue +2 -1
  58. package/layers/shader/app/components/Preset/ThemePlasma.client.vue +2 -1
  59. package/layers/shader/app/components/Preset/ThemeWave.client.vue +2 -1
  60. package/layers/shader/app/components/Shader/Background.client.vue +44 -24
  61. package/layers/shader/app/composables/useAmbientMaterials.ts +5 -1
  62. package/layers/shader/app/composables/useShader.ts +38 -23
  63. package/layers/shader/app/composables/useShaderGraph.ts +11 -6
  64. package/layers/shader/app/composables/useShaderMixBlend.ts +4 -4
  65. package/layers/shader/app/composables/useShaderRuntime.ts +0 -1
  66. package/layers/shader/app/composables/useShaderVec2.ts +2 -4
  67. package/layers/shader/app/composables/useThemePreset.ts +34 -8
  68. package/layers/shader/app/composables/useUniformWatchers.ts +15 -0
  69. package/layers/shader/app/composables/useUniforms.ts +0 -1
  70. package/layers/shader/app/shaders/common/blend.ts +4 -4
  71. package/layers/shader/app/shaders/common/effects.ts +38 -21
  72. package/layers/shader/app/shaders/common/grain.ts +46 -49
  73. package/layers/shader/app/shaders/common/lighting.ts +17 -15
  74. package/layers/shader/app/shaders/common/math.ts +2 -4
  75. package/layers/shader/app/shaders/common/nodes.ts +17 -0
  76. package/layers/shader/app/shaders/common/palette.ts +21 -11
  77. package/layers/shader/app/shaders/common/patterns.ts +25 -14
  78. package/layers/shader/app/shaders/common/shapes.ts +97 -88
  79. package/layers/shader/app/shaders/common/uv.ts +33 -34
  80. package/layers/shader/app/shaders/createMaterial.ts +92 -78
  81. package/layers/shader/app/shaders/layers/paperShading.ts +22 -10
  82. package/layers/shader/app/shaders/layers/shaderGradient.ts +46 -21
  83. package/layers/shader/app/utils/tsl/tween.ts +2 -4
  84. package/layers/shader/package.json +5 -1
  85. package/layers/theme/app/components/ThemePicker/Menu.vue +3 -25
  86. package/layers/theme/app/composables/useThemePreferenceModels.ts +39 -0
  87. package/layers/theme/server/plugins/theme-fouc.ts +1 -92
  88. package/layers/theme/server/utils/accent-css.ts +75 -0
  89. package/layers/typography/app/composables/typography.ts +3 -7
  90. package/layers/visual/app/composables/accent.ts +2 -9
  91. package/layers/visual/app/composables/gradient.ts +33 -46
  92. package/layers/visual/app/composables/picture.ts +2 -79
  93. package/layers/visual/app/utils/colorTokens.ts +23 -0
  94. package/layers/visual/app/utils/gradientStyle.ts +41 -0
  95. package/layers/visual/app/utils/responsiveSizes.ts +49 -0
  96. package/package.json +15 -4
  97. package/layers/feeds/app/utils/feed-catalog.test.ts +0 -71
  98. package/layers/feeds/server/routes/feed/discovery.get.ts +0 -31
@@ -13,7 +13,10 @@ export type ShaderGraphEntry = {
13
13
  }
14
14
 
15
15
  export type ShaderGraph = {
16
- register: (id: string, node: TSLNode, order?: number, blend?: BlendMode, opacity?: number) => void
16
+ register: (
17
+ id: string,
18
+ ...args: [node: TSLNode, order?: number, blend?: BlendMode, opacity?: number]
19
+ ) => void
17
20
  unregister: (id: string) => void
18
21
  update: (id: string, node: TSLNode) => void
19
22
  finalNode: ComputedRef<TSLNode | null>
@@ -33,11 +36,9 @@ export function useShaderGraph(): ShaderGraph {
33
36
 
34
37
  function register(
35
38
  id: string,
36
- node: TSLNode,
37
- order = 0,
38
- blend: BlendMode = 'normal',
39
- opacity = 1.0
39
+ ...args: [node: TSLNode, order?: number, blend?: BlendMode, opacity?: number]
40
40
  ) {
41
+ const [node, order = 0, blend = 'normal', opacity = 1.0] = args
41
42
  entries.set(id, { node, order, blend, opacity })
42
43
  version.value++
43
44
  }
@@ -99,7 +100,11 @@ export function useShaderGraphContext(): ShaderGraph {
99
100
  return graph
100
101
  }
101
102
 
102
- function applyBlend(base: TSLNode, layer: TSLNode, mode: BlendMode, opacity: number): TSLNode {
103
+ function applyBlend(
104
+ base: TSLNode,
105
+ ...args: [layer: TSLNode, mode: BlendMode, opacity?: number]
106
+ ): TSLNode {
107
+ const [layer, mode, opacity = 1] = args
103
108
  let blended: TSLNode
104
109
 
105
110
  switch (mode) {
@@ -5,10 +5,9 @@ import type { TSLNode } from '../types/tsl'
5
5
 
6
6
  export function useShaderMixBlend(
7
7
  blendFn: (base: TSLNode, blend: TSLNode) => TSLNode,
8
- colorGetter: () => string,
9
- opacityGetter: () => number,
10
- order: number = 0
8
+ ...args: [colorGetter: () => string, opacityGetter: () => number, order?: number]
11
9
  ) {
10
+ const [colorGetter, opacityGetter, order = 0] = args
12
11
  const colorNode = uniform(new Color(colorGetter()))
13
12
  const opacityNode = uniform(opacityGetter())
14
13
 
@@ -20,7 +19,8 @@ export function useShaderMixBlend(
20
19
  })
21
20
 
22
21
  useShaderStage(
23
- (prev) => vec4(mix(prev.xyz, blendFn(prev.xyz, colorNode as unknown as TSLNode), opacityNode), prev.w),
22
+ (prev) =>
23
+ vec4(mix(prev.xyz, blendFn(prev.xyz, colorNode as unknown as TSLNode), opacityNode), prev.w),
24
24
  order
25
25
  )
26
26
  }
@@ -2,7 +2,6 @@ import type { InjectionKey } from 'vue'
2
2
  import { Color } from 'three'
3
3
  import { uniform } from 'three/tsl'
4
4
 
5
-
6
5
  export type ShaderRuntimeOptions = {
7
6
  speed?: number
8
7
  accentColor?: string
@@ -28,11 +28,9 @@ export function useShaderVec2(initialX = 0, initialY = 0) {
28
28
  }
29
29
 
30
30
  function tweenTo(
31
- targetX: number,
32
- targetY: number,
33
- duration: number,
34
- easing?: (t: number) => number
31
+ ...args: [targetX: number, targetY: number, duration: number, easing?: (t: number) => number]
35
32
  ): TweenHandle {
33
+ const [targetX, targetY, duration, easing] = args
36
34
  activeTween?.cancel()
37
35
 
38
36
  const startX = vector.x
@@ -41,10 +41,22 @@ export function useThemePreset(
41
41
  color4: c4.node,
42
42
  })
43
43
 
44
- watch(() => props.color1, (hex) => c1.tweenTo(hex, 0.8))
45
- watch(() => props.color2, (hex) => c2.tweenTo(hex, 0.8))
46
- watch(() => props.color3, (hex) => c3.tweenTo(hex, 0.8))
47
- watch(() => props.color4, (hex) => c4.tweenTo(hex, 0.8))
44
+ watch(
45
+ () => props.color1,
46
+ (hex) => c1.tweenTo(hex, 0.8)
47
+ )
48
+ watch(
49
+ () => props.color2,
50
+ (hex) => c2.tweenTo(hex, 0.8)
51
+ )
52
+ watch(
53
+ () => props.color3,
54
+ (hex) => c3.tweenTo(hex, 0.8)
55
+ )
56
+ watch(
57
+ () => props.color4,
58
+ (hex) => c4.tweenTo(hex, 0.8)
59
+ )
48
60
 
49
61
  try {
50
62
  const runtime = useShaderRuntimeContext()
@@ -60,15 +72,29 @@ export function useThemePreset(
60
72
  // No runtime context
61
73
  }
62
74
 
63
- watch(() => props.speed, (v) => { uniforms.speed.value = v })
64
- watch(() => props.intensity, (v) => { uniforms.intensity.value = v })
75
+ watch(
76
+ () => props.speed,
77
+ (v) => {
78
+ uniforms.speed.value = v
79
+ }
80
+ )
81
+ watch(
82
+ () => props.intensity,
83
+ (v) => {
84
+ uniforms.intensity.value = v
85
+ }
86
+ )
65
87
  watch(
66
88
  () => props.mouseInteraction,
67
- (v) => { uniforms.mouseStrength.value = v ? props.mouseStrength : 0 }
89
+ (v) => {
90
+ uniforms.mouseStrength.value = v ? props.mouseStrength : 0
91
+ }
68
92
  )
69
93
  watch(
70
94
  () => props.mouseStrength,
71
- (v) => { if (props.mouseInteraction) uniforms.mouseStrength.value = v }
95
+ (v) => {
96
+ if (props.mouseInteraction) uniforms.mouseStrength.value = v
97
+ }
72
98
  )
73
99
 
74
100
  return { uniforms, colorNode }
@@ -0,0 +1,15 @@
1
+ import { watch } from 'vue'
2
+
3
+ type WritableUniform<T> = {
4
+ value: T
5
+ }
6
+
7
+ export function watchUniformProp<T, U = T>(
8
+ source: () => T,
9
+ target: WritableUniform<U>,
10
+ transform?: (value: T) => U
11
+ ) {
12
+ watch(source, (value) => {
13
+ target.value = transform ? transform(value) : (value as unknown as U)
14
+ })
15
+ }
@@ -1,4 +1,3 @@
1
-
2
1
  import { reactive, ref, watch } from 'vue'
3
2
  import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three'
4
3
  import { uniform } from 'three/tsl'
@@ -5,6 +5,7 @@
5
5
  import { abs, clamp, float, max, min, mix, step, vec3 } from 'three/tsl'
6
6
 
7
7
  import type { TSLNode } from '../types'
8
+ import { toScalarNode } from './nodes'
8
9
 
9
10
  // ============================================
10
11
  // Basic Blending
@@ -168,11 +169,10 @@ export function blendPinLight(base: TSLNode, blend: TSLNode): TSLNode {
168
169
  */
169
170
  export function blendWithOpacity(
170
171
  base: TSLNode,
171
- blend: TSLNode,
172
- blendFn: (a: TSLNode, b: TSLNode) => TSLNode,
173
- opacity: TSLNode | number = 1
172
+ ...args: [blend: TSLNode, blendFn: (a: TSLNode, b: TSLNode) => TSLNode, opacity?: TSLNode | number]
174
173
  ): TSLNode {
175
- const op = typeof opacity === 'number' ? float(opacity) : opacity
174
+ const [blend, blendFn, opacity = 1] = args
175
+ const op = toScalarNode(opacity)
176
176
  const blended = blendFn(base, blend)
177
177
  return mix(base, blended, op)
178
178
  }
@@ -17,6 +17,7 @@ import {
17
17
  } from 'three/tsl'
18
18
 
19
19
  import type { TSLNode } from '../types'
20
+ import { toScalarNode, toVec2Node } from './nodes'
20
21
 
21
22
  // ============================================
22
23
  // LED Effect
@@ -29,6 +30,13 @@ export type LEDEffectOptions = {
29
30
  edge?: TSLNode | number
30
31
  }
31
32
 
33
+ const DEFAULT_LED_EFFECT_OPTIONS = {
34
+ scalar: 100,
35
+ zoom: 2,
36
+ exponent: 1.2,
37
+ edge: 0.1,
38
+ } as const
39
+
32
40
  /**
33
41
  * LED screen effect for post-processing
34
42
  * Creates a grid of circular LED dots over the input texture
@@ -41,12 +49,15 @@ export function ledEffect(
41
49
  inputUV: () => TSLNode = uv,
42
50
  options: LEDEffectOptions = {}
43
51
  ): TSLNode {
44
- const { scalar = 100, zoom = 2, exponent = 1.2, edge = 0.1 } = options
52
+ const { scalar, zoom, exponent, edge } = {
53
+ ...DEFAULT_LED_EFFECT_OPTIONS,
54
+ ...options,
55
+ }
45
56
 
46
- const _scalar = typeof scalar === 'number' ? float(scalar) : scalar
47
- const _zoom = typeof zoom === 'number' ? float(zoom) : zoom
48
- const _exponent = typeof exponent === 'number' ? float(exponent) : exponent
49
- const _edge = typeof edge === 'number' ? float(edge) : edge
57
+ const _scalar = toScalarNode(scalar)
58
+ const _zoom = toScalarNode(zoom)
59
+ const _exponent = toScalarNode(exponent)
60
+ const _edge = toScalarNode(edge)
50
61
 
51
62
  const _uv = inputUV().toVar()
52
63
 
@@ -168,6 +179,13 @@ export type BulgeEffectOptions = {
168
179
  center?: TSLNode | [number, number]
169
180
  }
170
181
 
182
+ const DEFAULT_BULGE_EFFECT_OPTIONS = {
183
+ strength: 0.5,
184
+ radius: 0.5,
185
+ power: 1,
186
+ center: [0.5, 0.5] as [number, number],
187
+ } as const
188
+
171
189
  /**
172
190
  * Bulge/pinch distortion effect for post-processing
173
191
  * @param input - Input texture
@@ -179,12 +197,12 @@ export function bulgeEffect(
179
197
  inputUV: () => TSLNode = uv,
180
198
  options: BulgeEffectOptions = {}
181
199
  ): TSLNode {
182
- const { strength = 0.5, radius = 0.5, power = 1.0, center = [0.5, 0.5] } = options
200
+ const { strength, radius, power, center } = { ...DEFAULT_BULGE_EFFECT_OPTIONS, ...options }
183
201
 
184
- const _strength = typeof strength === 'number' ? float(strength) : strength
185
- const _radius = typeof radius === 'number' ? float(radius) : radius
186
- const _power = typeof power === 'number' ? float(power) : power
187
- const _center = Array.isArray(center) ? vec2(center[0], center[1]) : center
202
+ const _strength = toScalarNode(strength)
203
+ const _radius = toScalarNode(radius)
204
+ const _power = toScalarNode(power)
205
+ const _center = toVec2Node(center)
188
206
 
189
207
  const _uv = inputUV().toVar()
190
208
  const offset = _uv.sub(_center).toVar()
@@ -226,24 +244,23 @@ export function waveDistortionEffect(
226
244
  options: WaveDistortionOptions = {}
227
245
  ): TSLNode {
228
246
  const { frequency = 10, amplitude = 0.02, time = 0, direction = 'both' } = options
247
+ type WaveDirection = NonNullable<WaveDistortionOptions['direction']>
229
248
 
230
- const _freq = typeof frequency === 'number' ? float(frequency) : frequency
231
- const _amp = typeof amplitude === 'number' ? float(amplitude) : amplitude
232
- const _time = typeof time === 'number' ? float(time) : time
249
+ const _freq = toScalarNode(frequency)
250
+ const _amp = toScalarNode(amplitude)
251
+ const _time = toScalarNode(time)
233
252
 
234
253
  const _uv = inputUV().toVar()
235
254
 
236
255
  const _eX = _uv.y.mul(_freq).add(_time).sin().mul(_amp)
237
256
  const _eY = _uv.x.mul(_freq).add(_time.mul(1.3)).sin().mul(_amp)
238
- let distortedUV: TSLNode = vec2(_uv.x.add(_eX), _uv.y.add(_eY))
239
-
240
- if (direction === 'x') {
241
- distortedUV = vec2(_uv.x.add(_eX), _uv.y)
242
- } else if (direction === 'y') {
243
- distortedUV = vec2(_uv.x, _uv.y.add(_uv.x.mul(_freq).add(_time).sin().mul(_amp)))
244
- }
257
+ const distortedUVMap = {
258
+ both: vec2(_uv.x.add(_eX), _uv.y.add(_eY)),
259
+ x: vec2(_uv.x.add(_eX), _uv.y),
260
+ y: vec2(_uv.x, _uv.y.add(_uv.x.mul(_freq).add(_time).sin().mul(_amp))),
261
+ } satisfies Record<WaveDirection, TSLNode>
245
262
 
246
- return input.sample(distortedUV)
263
+ return input.sample(distortedUVMap[direction])
247
264
  }
248
265
 
249
266
  // ============================================
@@ -5,6 +5,7 @@
5
5
  import { clamp, dot, float, floor, fract, mix, sin, smoothstep, vec2, vec3 } from 'three/tsl'
6
6
 
7
7
  import type { TSLNode } from '../types'
8
+ import { toScalarNode } from './nodes'
8
9
 
9
10
  // ============================================
10
11
  // Basic Grain
@@ -31,11 +32,11 @@ export function grain(
31
32
  export function animatedGrain(
32
33
  uv: TSLNode,
33
34
  time: TSLNode,
34
- intensity: TSLNode | number = 0.1,
35
- speed: TSLNode | number = 1
35
+ ...args: [intensity?: TSLNode | number, speed?: TSLNode | number]
36
36
  ): TSLNode {
37
- const int = typeof intensity === 'number' ? float(intensity) : intensity
38
- const spd = typeof speed === 'number' ? float(speed) : speed
37
+ const [intensity = 0.1, speed = 1] = args
38
+ const int = toScalarNode(intensity)
39
+ const spd = toScalarNode(speed)
39
40
 
40
41
  // Use time as seed for animation
41
42
  const seed = floor(time.mul(spd).mul(24)) // 24 fps grain
@@ -93,11 +94,11 @@ export function bayer4x4(uv: TSLNode, scale: TSLNode | number = 1): TSLNode {
93
94
  export function ditherColor(
94
95
  color: TSLNode,
95
96
  uv: TSLNode,
96
- levels: TSLNode | number = 16,
97
- strength: TSLNode | number = 1
97
+ ...args: [levels?: TSLNode | number, strength?: TSLNode | number]
98
98
  ): TSLNode {
99
- const lvl = typeof levels === 'number' ? float(levels) : levels
100
- const str = typeof strength === 'number' ? float(strength) : strength
99
+ const [levels = 16, strength = 1] = args
100
+ const lvl = toScalarNode(levels)
101
+ const str = toScalarNode(strength)
101
102
 
102
103
  const threshold = bayer4x4(uv, 4).mul(str).div(lvl)
103
104
  const dithered = floor(color.mul(lvl).add(threshold)).div(lvl)
@@ -114,13 +115,12 @@ export function ditherColor(
114
115
  */
115
116
  export function scanlines(
116
117
  uv: TSLNode,
117
- density: TSLNode | number = 400,
118
- intensity: TSLNode | number = 0.25,
119
- offset: TSLNode | number = 0
118
+ ...args: [density?: TSLNode | number, intensity?: TSLNode | number, offset?: TSLNode | number]
120
119
  ): TSLNode {
121
- const d = typeof density === 'number' ? float(density) : density
122
- const int = typeof intensity === 'number' ? float(intensity) : intensity
123
- const off = typeof offset === 'number' ? float(offset) : offset
120
+ const [density = 400, intensity = 0.25, offset = 0] = args
121
+ const d = toScalarNode(density)
122
+ const int = toScalarNode(intensity)
123
+ const off = toScalarNode(offset)
124
124
 
125
125
  const line = sin(uv.y.add(off).mul(d).mul(Math.PI)).mul(0.5).add(0.5)
126
126
  return float(1).sub(line.mul(int))
@@ -131,13 +131,12 @@ export function scanlines(
131
131
  */
132
132
  export function interlace(
133
133
  uv: TSLNode,
134
- density: TSLNode | number = 400,
135
- intensity: TSLNode | number = 0.5,
136
- frame: TSLNode | number = 0
134
+ ...args: [density?: TSLNode | number, intensity?: TSLNode | number, frame?: TSLNode | number]
137
135
  ): TSLNode {
138
- const d = typeof density === 'number' ? float(density) : density
139
- const int = typeof intensity === 'number' ? float(intensity) : intensity
140
- const f = typeof frame === 'number' ? float(frame) : frame
136
+ const [density = 400, intensity = 0.5, frame = 0] = args
137
+ const d = toScalarNode(density)
138
+ const int = toScalarNode(intensity)
139
+ const f = toScalarNode(frame)
141
140
 
142
141
  const line = floor(uv.y.mul(d)).add(f).mod(2)
143
142
  return float(1).sub(line.mul(int))
@@ -152,13 +151,12 @@ export function interlace(
152
151
  */
153
152
  export function vignette(
154
153
  uv: TSLNode,
155
- intensity: TSLNode | number = 0.5,
156
- smoothness: TSLNode | number = 0.5,
157
- roundness: TSLNode | number = 1
154
+ ...args: [intensity?: TSLNode | number, smoothness?: TSLNode | number, roundness?: TSLNode | number]
158
155
  ): TSLNode {
159
- const int = typeof intensity === 'number' ? float(intensity) : intensity
160
- const smooth = typeof smoothness === 'number' ? float(smoothness) : smoothness
161
- const round = typeof roundness === 'number' ? float(roundness) : roundness
156
+ const [intensity = 0.5, smoothness = 0.5, roundness = 1] = args
157
+ const int = toScalarNode(intensity)
158
+ const smooth = toScalarNode(smoothness)
159
+ const round = toScalarNode(roundness)
162
160
 
163
161
  const center = uv.sub(0.5)
164
162
  const dist = center.length().mul(round)
@@ -192,13 +190,12 @@ export function rectVignette(
192
190
  */
193
191
  export function paperTexture(
194
192
  uv: TSLNode,
195
- scale: TSLNode | number = 10,
196
- intensity: TSLNode | number = 0.1,
197
- seed: TSLNode | number = 0
193
+ ...args: [scale?: TSLNode | number, intensity?: TSLNode | number, seed?: TSLNode | number]
198
194
  ): TSLNode {
199
- const s = typeof scale === 'number' ? float(scale) : scale
200
- const int = typeof intensity === 'number' ? float(intensity) : intensity
201
- const sd = typeof seed === 'number' ? float(seed) : seed
195
+ const [scale = 10, intensity = 0.1, seed = 0] = args
196
+ const s = toScalarNode(scale)
197
+ const int = toScalarNode(intensity)
198
+ const sd = toScalarNode(seed)
202
199
 
203
200
  // Multi-octave noise for paper texture
204
201
  const noise1 = grain(uv.mul(s), 1, sd)
@@ -214,11 +211,11 @@ export function paperTexture(
214
211
  export function halftone(
215
212
  uv: TSLNode,
216
213
  value: TSLNode,
217
- scale: TSLNode | number = 50,
218
- angle: TSLNode | number = 0
214
+ ...args: [scale?: TSLNode | number, angle?: TSLNode | number]
219
215
  ): TSLNode {
220
- const s = typeof scale === 'number' ? float(scale) : scale
221
- const a = typeof angle === 'number' ? float(angle) : angle
216
+ const [scale = 50, angle = 0] = args
217
+ const s = toScalarNode(scale)
218
+ const a = toScalarNode(angle)
222
219
 
223
220
  // Rotate UV
224
221
  const cosA = a.cos()
@@ -245,9 +242,9 @@ export function halftone(
245
242
  export function applyGrain(
246
243
  color: TSLNode,
247
244
  uv: TSLNode,
248
- intensity: TSLNode | number = 0.05,
249
- seed: TSLNode | number = 0
245
+ ...args: [intensity?: TSLNode | number, seed?: TSLNode | number]
250
246
  ): TSLNode {
247
+ const [intensity = 0.05, seed = 0] = args
251
248
  const grainValue = grain(uv, intensity, seed)
252
249
  return clamp(color.add(grainValue), 0, 1)
253
250
  }
@@ -258,9 +255,9 @@ export function applyGrain(
258
255
  export function applyColoredGrain(
259
256
  color: TSLNode,
260
257
  uv: TSLNode,
261
- intensity: TSLNode | number = 0.05,
262
- seed: TSLNode | number = 0
258
+ ...args: [intensity?: TSLNode | number, seed?: TSLNode | number]
263
259
  ): TSLNode {
260
+ const [intensity = 0.05, seed = 0] = args
264
261
  const grainValue = coloredGrain(uv, intensity, seed)
265
262
  return clamp(color.add(grainValue), 0, 1)
266
263
  }
@@ -271,8 +268,9 @@ export function applyColoredGrain(
271
268
  export function applyVignette(
272
269
  color: TSLNode,
273
270
  uv: TSLNode,
274
- intensity: TSLNode | number = 0.5
271
+ ...args: [intensity?: TSLNode | number]
275
272
  ): TSLNode {
273
+ const [intensity = 0.5] = args
276
274
  const vignetteValue = vignette(uv, intensity)
277
275
  return color.mul(vignetteValue)
278
276
  }
@@ -283,9 +281,9 @@ export function applyVignette(
283
281
  export function applyScanlines(
284
282
  color: TSLNode,
285
283
  uv: TSLNode,
286
- density: TSLNode | number = 400,
287
- intensity: TSLNode | number = 0.25
284
+ ...args: [density?: TSLNode | number, intensity?: TSLNode | number]
288
285
  ): TSLNode {
286
+ const [density = 400, intensity = 0.25] = args
289
287
  const scanlineValue = scanlines(uv, density, intensity)
290
288
  return color.mul(scanlineValue)
291
289
  }
@@ -437,13 +435,12 @@ export function bayer8x8(uv: TSLNode, scale: TSLNode | number = 1): TSLNode {
437
435
  export function dither8x8Color(
438
436
  color: TSLNode,
439
437
  uv: TSLNode,
440
- levels: TSLNode | number = 16,
441
- strength: TSLNode | number = 1,
442
- bias: TSLNode | number = 0.5
438
+ ...args: [levels?: TSLNode | number, strength?: TSLNode | number, bias?: TSLNode | number]
443
439
  ): TSLNode {
444
- const lvl = typeof levels === 'number' ? float(levels) : levels
445
- const str = typeof strength === 'number' ? float(strength) : strength
446
- const b = typeof bias === 'number' ? float(bias) : bias
440
+ const [levels = 16, strength = 1, bias = 0.5] = args
441
+ const lvl = toScalarNode(levels)
442
+ const str = toScalarNode(strength)
443
+ const b = toScalarNode(bias)
447
444
 
448
445
  const threshold = bayer8x8(uv, 8).sub(b).mul(str).div(lvl)
449
446
  const dithered = floor(color.mul(lvl).add(threshold)).div(lvl)
@@ -5,6 +5,7 @@
5
5
  import { dot, float, max, mix, normalize, pow, reflect, vec3 } from 'three/tsl'
6
6
 
7
7
  import type { TSLNode } from '../types'
8
+ import { toScalarNode } from './nodes'
8
9
 
9
10
  // ============================================
10
11
  // Fresnel Effect
@@ -72,11 +73,10 @@ export function diffuse(lightDir: TSLNode, normal: TSLNode, lightColor: TSLNode)
72
73
  */
73
74
  export function phongSpecular(
74
75
  viewDir: TSLNode,
75
- normal: TSLNode,
76
- lightDir: TSLNode,
77
- shininess: TSLNode | number = 32
76
+ ...args: [normal: TSLNode, lightDir: TSLNode, shininess?: TSLNode | number]
78
77
  ): TSLNode {
79
- const p = typeof shininess === 'number' ? float(shininess) : shininess
78
+ const [normal, lightDir, shininess = 32] = args
79
+ const p = toScalarNode(shininess)
80
80
  const reflectDir = normalize(reflect(lightDir.negate(), normal))
81
81
  const phongValue = max(0, dot(viewDir, reflectDir)).pow(p)
82
82
  return vec3(phongValue)
@@ -96,11 +96,10 @@ export function phongSpecular(
96
96
  */
97
97
  export function blinnPhongSpecular(
98
98
  viewDir: TSLNode,
99
- normal: TSLNode,
100
- lightDir: TSLNode,
101
- shininess: TSLNode | number = 32
99
+ ...args: [normal: TSLNode, lightDir: TSLNode, shininess?: TSLNode | number]
102
100
  ): TSLNode {
103
- const p = typeof shininess === 'number' ? float(shininess) : shininess
101
+ const [normal, lightDir, shininess = 32] = args
102
+ const p = toScalarNode(shininess)
104
103
  const halfDir = normalize(lightDir.add(viewDir))
105
104
  const specAngle = max(0, dot(halfDir, normal))
106
105
  return vec3(pow(specAngle, p))
@@ -127,16 +126,19 @@ export type LightingOptions = {
127
126
  */
128
127
  export function phongLighting(
129
128
  viewDir: TSLNode,
130
- normal: TSLNode,
131
- lightDir: TSLNode,
132
- lightColor: TSLNode,
133
- options: LightingOptions = {}
129
+ ...args: [
130
+ normal: TSLNode,
131
+ lightDir: TSLNode,
132
+ lightColor: TSLNode,
133
+ options?: LightingOptions,
134
+ ]
134
135
  ): TSLNode {
136
+ const [normal, lightDir, lightColor, options = {}] = args
135
137
  const { ambient = 0.1, diffuseStrength = 1.0, specularStrength = 0.5, shininess = 32 } = options
136
138
 
137
- const ambientVal = typeof ambient === 'number' ? float(ambient) : ambient
138
- const diffStr = typeof diffuseStrength === 'number' ? float(diffuseStrength) : diffuseStrength
139
- const specStr = typeof specularStrength === 'number' ? float(specularStrength) : specularStrength
139
+ const ambientVal = toScalarNode(ambient)
140
+ const diffStr = toScalarNode(diffuseStrength)
141
+ const specStr = toScalarNode(specularStrength)
140
142
 
141
143
  const ambientLight = lightColor.mul(ambientVal)
142
144
  const diffuseLight = diffuse(lightDir, normal, lightColor).mul(diffStr)
@@ -167,11 +167,9 @@ export function complexTan(a: TSLNode): TSLNode {
167
167
  */
168
168
  export function grad(
169
169
  st: TSLNode,
170
- color1: TSLNode,
171
- color2: TSLNode,
172
- color3: TSLNode,
173
- color4: TSLNode
170
+ ...colors: [color1: TSLNode, color2: TSLNode, color3: TSLNode, color4: TSLNode]
174
171
  ): TSLNode {
172
+ const [color1, color2, color3, color4] = colors
175
173
  const _uv = vec2(st).toVar()
176
174
  const _color0 = vec4(color1, 1.0).toVar()
177
175
  const _color1 = vec4(color2, 1.0).toVar()
@@ -0,0 +1,17 @@
1
+ import { float, vec2, vec3 } from 'three/tsl'
2
+
3
+ import type { TSLNode } from '../types'
4
+
5
+ export function toScalarNode(value: TSLNode | number): TSLNode {
6
+ return typeof value === 'number' ? float(value) : value
7
+ }
8
+
9
+ export function toVec2Node(value: TSLNode | number | [number, number]): TSLNode {
10
+ if (typeof value === 'number') return vec2(value, value)
11
+ if (Array.isArray(value)) return vec2(value[0], value[1])
12
+ return value
13
+ }
14
+
15
+ export function toVec3Node(value: TSLNode | [number, number, number]): TSLNode {
16
+ return Array.isArray(value) ? vec3(value[0], value[1], value[2]) : value
17
+ }
@@ -48,10 +48,9 @@ export function gradient2(t: TSLNode, color1: TSLNode | string, color2: TSLNode
48
48
  */
49
49
  export function gradient3(
50
50
  t: TSLNode,
51
- color1: TSLNode | string,
52
- color2: TSLNode | string,
53
- color3: TSLNode | string
51
+ ...colors: [color1: TSLNode | string, color2: TSLNode | string, color3: TSLNode | string]
54
52
  ): TSLNode {
53
+ const [color1, color2, color3] = colors
55
54
  const c1 = typeof color1 === 'string' ? hexToVec3(color1) : color1
56
55
  const c2 = typeof color2 === 'string' ? hexToVec3(color2) : color2
57
56
  const c3 = typeof color3 === 'string' ? hexToVec3(color3) : color3
@@ -68,11 +67,14 @@ export function gradient3(
68
67
  */
69
68
  export function gradient4(
70
69
  t: TSLNode,
71
- color1: TSLNode | string,
72
- color2: TSLNode | string,
73
- color3: TSLNode | string,
74
- color4: TSLNode | string
70
+ ...colors: [
71
+ color1: TSLNode | string,
72
+ color2: TSLNode | string,
73
+ color3: TSLNode | string,
74
+ color4: TSLNode | string,
75
+ ]
75
76
  ): TSLNode {
77
+ const [color1, color2, color3, color4] = colors
76
78
  const c1 = typeof color1 === 'string' ? hexToVec3(color1) : color1
77
79
  const c2 = typeof color2 === 'string' ? hexToVec3(color2) : color2
78
80
  const c3 = typeof color3 === 'string' ? hexToVec3(color3) : color3
@@ -133,11 +135,19 @@ export function gradientMulti(t: TSLNode, stops: ColorStop[]): TSLNode {
133
135
  */
134
136
  export function cosinePalette(
135
137
  t: TSLNode,
136
- brightness: TSLNode | [number, number, number] = [0.5, 0.5, 0.5],
137
- contrast: TSLNode | [number, number, number] = [0.5, 0.5, 0.5],
138
- frequency: TSLNode | [number, number, number] = [1.0, 1.0, 1.0],
139
- phase: TSLNode | [number, number, number] = [0.0, 0.33, 0.67]
138
+ ...args: [
139
+ brightness?: TSLNode | [number, number, number],
140
+ contrast?: TSLNode | [number, number, number],
141
+ frequency?: TSLNode | [number, number, number],
142
+ phase?: TSLNode | [number, number, number],
143
+ ]
140
144
  ): TSLNode {
145
+ const [
146
+ brightness = [0.5, 0.5, 0.5],
147
+ contrast = [0.5, 0.5, 0.5],
148
+ frequency = [1.0, 1.0, 1.0],
149
+ phase = [0.0, 0.33, 0.67],
150
+ ] = args
141
151
  const a = Array.isArray(brightness) ? vec3(...brightness) : brightness
142
152
  const b = Array.isArray(contrast) ? vec3(...contrast) : contrast
143
153
  const c = Array.isArray(frequency) ? vec3(...frequency) : frequency