@lovo/matter 0.5.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @lovo/matter
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 24ec05d: Add color-space-aware interpolation. `colorRamp` and the new `mixColor` primitive
8
+ accept `colorSpace` ('linear' | 'oklab' | 'oklch' | 'lch' | 'hsl' | 'hsv',
9
+ default 'oklab') and `hueInterpolation` ('shorter' | 'longer' | 'increasing' |
10
+ 'decreasing', default 'shorter'). LinearGradient, SimplexNoise, and MeshGradient
11
+ gain matching props. Foundation fix: hex colors now decode to linear-sRGB (true
12
+ color), and the LCH conversion's green coefficient was corrected. This shifts the
13
+ default appearance of those components (pre-1.0 breaking color change).
14
+
3
15
  ## 0.5.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
19
19
 
20
20
  // src/index.ts
21
21
  var index_exports = {};
@@ -29,29 +29,68 @@ __export(index_exports, {
29
29
  createVisibilityWatcher: () => createVisibilityWatcher,
30
30
  cursorRipple: () => cursorRipple,
31
31
  displace: () => displace,
32
+ dither: () => dither,
32
33
  elapsedTime: () => elapsedTime,
33
34
  filmGrain: () => filmGrain,
34
35
  fractalNoise: () => fractalNoise,
35
36
  getReducedMotionPolicy: () => getReducedMotionPolicy,
36
37
  getReducedMotionTimeScale: () => getReducedMotionTimeScale,
38
+ mixColor: () => mixColor,
39
+ oklabToLinearSrgb: () => oklabToLinearSrgb,
40
+ oklchToLinearSrgb: () => oklchToLinearSrgb,
41
+ parseColorString: () => parseColorString,
37
42
  quantize: () => quantize,
38
43
  setReducedMotionPolicy: () => setReducedMotionPolicy,
39
44
  signedDistanceFieldCircle: () => signedDistanceFieldCircle,
40
45
  simplexNoise: () => simplexNoise,
46
+ srgbChannelToLinear: () => srgbChannelToLinear,
41
47
  voronoi: () => voronoi
42
48
  });
43
49
  module.exports = __toCommonJS(index_exports);
44
50
 
45
51
  // src/runtime/create-renderer/create-renderer.ts
46
- var import_three = require("three");
52
+ var import_three2 = require("three");
47
53
  var import_webgpu = require("three/webgpu");
54
+
55
+ // src/runtime/create-renderer/gamut.ts
56
+ var import_three = require("three");
57
+ var import_ColorSpaces = require("three/examples/jsm/math/ColorSpaces.js");
58
+ import_three.ColorManagement.define({
59
+ [import_ColorSpaces.DisplayP3ColorSpace]: import_ColorSpaces.DisplayP3ColorSpaceImpl,
60
+ [import_ColorSpaces.LinearDisplayP3ColorSpace]: import_ColorSpaces.LinearDisplayP3ColorSpaceImpl
61
+ });
62
+ function gamutToColorSpace(gamut) {
63
+ return gamut === "p3" ? import_ColorSpaces.DisplayP3ColorSpace : import_three.SRGBColorSpace;
64
+ }
65
+ function hasWebGpuBackendInternals(backend) {
66
+ if (typeof backend !== "object" || backend === null) return false;
67
+ if (!("device" in backend) || !("context" in backend)) return false;
68
+ const { device, context } = backend;
69
+ return typeof device === "object" && device !== null && typeof context === "object" && context !== null && "configure" in context && typeof context.configure === "function";
70
+ }
71
+ function applyCanvasGamut(renderer, backend, gamut) {
72
+ if (gamut !== "p3" || backend !== "webgpu") return;
73
+ if (typeof navigator === "undefined" || !("gpu" in navigator)) return;
74
+ const webGpuBackend = renderer.backend;
75
+ if (!hasWebGpuBackendInternals(webGpuBackend)) return;
76
+ webGpuBackend.context.configure({
77
+ device: webGpuBackend.device,
78
+ format: navigator.gpu.getPreferredCanvasFormat(),
79
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
80
+ alphaMode: "premultiplied",
81
+ colorSpace: "display-p3"
82
+ });
83
+ }
84
+
85
+ // src/runtime/create-renderer/create-renderer.ts
48
86
  async function createRenderer(canvas, opts = {}) {
49
87
  const {
50
88
  antialias = true,
51
89
  forceWebGL = false,
52
90
  clearColor = 0,
53
91
  clearAlpha = 0,
54
- maxDPR = 2
92
+ maxDPR = 2,
93
+ gamut = "srgb"
55
94
  } = opts;
56
95
  const three = new import_webgpu.WebGPURenderer({
57
96
  canvas,
@@ -59,19 +98,24 @@ async function createRenderer(canvas, opts = {}) {
59
98
  forceWebGL
60
99
  });
61
100
  await three.init();
101
+ three.outputColorSpace = gamutToColorSpace(gamut);
62
102
  three.setPixelRatio(Math.min(window.devicePixelRatio, maxDPR));
63
- const resolvedClearColor = clearColor instanceof import_three.Color ? clearColor : new import_three.Color(clearColor);
103
+ const resolvedClearColor = clearColor instanceof import_three2.Color ? clearColor : new import_three2.Color(clearColor);
64
104
  three.setClearColor(resolvedClearColor, clearAlpha);
105
+ const rendererSize = new import_three2.Vector2();
65
106
  const resize = () => {
66
107
  const canvasWidth = canvas.clientWidth;
67
108
  const canvasHeight = canvas.clientHeight;
68
- if (canvas.width !== canvasWidth * three.getPixelRatio() || canvas.height !== canvasHeight * three.getPixelRatio()) {
109
+ if (canvasWidth === 0 || canvasHeight === 0) return;
110
+ three.getSize(rendererSize);
111
+ if (rendererSize.width !== canvasWidth || rendererSize.height !== canvasHeight) {
69
112
  three.setSize(canvasWidth, canvasHeight, false);
70
113
  }
71
114
  };
72
115
  resize();
73
116
  const isWebGL = "isWebGLBackend" in three.backend && three.backend.isWebGLBackend === true;
74
117
  const backend = forceWebGL || isWebGL ? "webgl2" : "webgpu";
118
+ applyCanvasGamut(three, backend, gamut);
75
119
  return {
76
120
  three,
77
121
  backend,
@@ -158,33 +202,369 @@ var clamp01 = (value) => Math.max(0, Math.min(1, value));
158
202
  var lerp = (startValue, endValue, blendFactor) => startValue + (endValue - startValue) * blendFactor;
159
203
 
160
204
  // src/primitives/color-ramp/color-ramp.ts
205
+ var import_tsl9 = require("three/tsl");
206
+
207
+ // src/primitives/color-space/hue.ts
161
208
  var import_tsl = require("three/tsl");
209
+ var EQUAL_HUE_EPSILON = 1e-6;
210
+ var shortestArcHue = (h1, h2, t, period) => {
211
+ const half = period / 2;
212
+ const delta = (0, import_tsl.mod)(h2.sub(h1).add(half), period).sub(half);
213
+ return h1.add(delta.mul(t));
214
+ };
215
+ var longestArcHue = (h1, h2, t, period) => {
216
+ const half = period / 2;
217
+ const short = (0, import_tsl.mod)(h2.sub(h1).add(half), period).sub(half);
218
+ const delta = short.sub((0, import_tsl.sign)(short).mul(period));
219
+ return h1.add(delta.mul(t));
220
+ };
221
+ var increasingArcHue = (h1, h2, t, period) => {
222
+ const delta = (0, import_tsl.mod)(h2.sub(h1), period);
223
+ return h1.add(delta.mul(t));
224
+ };
225
+ var decreasingArcHue = (h1, h2, t, period) => {
226
+ const up = (0, import_tsl.mod)(h2.sub(h1), period);
227
+ const delta = up.sub((0, import_tsl.step)(EQUAL_HUE_EPSILON, up).mul(period));
228
+ return h1.add(delta.mul(t));
229
+ };
230
+ var hueArcInterpolators = {
231
+ shorter: shortestArcHue,
232
+ longer: longestArcHue,
233
+ increasing: increasingArcHue,
234
+ decreasing: decreasingArcHue
235
+ };
236
+
237
+ // src/primitives/color-space/hsl.ts
238
+ var import_tsl3 = require("three/tsl");
239
+
240
+ // src/primitives/color-space/transfer.ts
162
241
  var import_tsl2 = require("three/tsl");
163
- function colorRamp(t, stops) {
242
+ function srgbChannelToLinear(channel) {
243
+ return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
244
+ }
245
+ function srgbToLinear(srgb) {
246
+ const value = (0, import_tsl2.pow)(srgb, 1);
247
+ const lowSegment = value.div(12.92);
248
+ const highSegment = (0, import_tsl2.pow)(value.add(0.055).div(1.055), 2.4);
249
+ return (0, import_tsl2.mix)(lowSegment, highSegment, (0, import_tsl2.step)(0.04045, value));
250
+ }
251
+ function linearToSrgb(linear) {
252
+ const value = (0, import_tsl2.pow)(linear, 1);
253
+ const lowSegment = value.mul(12.92);
254
+ const highSegment = (0, import_tsl2.pow)(value, 1 / 2.4).mul(1.055).sub(0.055);
255
+ return (0, import_tsl2.mix)(lowSegment, highSegment, (0, import_tsl2.step)(31308e-7, value));
256
+ }
257
+
258
+ // src/primitives/color-space/hsl.ts
259
+ var EPSILON = 1e-10;
260
+ function gammaRgbHue(c) {
261
+ const p = (0, import_tsl3.mix)((0, import_tsl3.vec4)(c.b, c.g, -1 / 3, 2 / 3), (0, import_tsl3.vec4)(c.g, c.b, 0, -1 / 3), (0, import_tsl3.step)(c.b, c.g));
262
+ const q = (0, import_tsl3.mix)((0, import_tsl3.vec4)(p.x, p.y, p.w, c.r), (0, import_tsl3.vec4)(c.r, p.y, p.z, p.x), (0, import_tsl3.step)(p.x, c.r));
263
+ const chroma = q.x.sub((0, import_tsl3.min)(q.w, q.y));
264
+ return (0, import_tsl3.abs)(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON))));
265
+ }
266
+ function gammaRgbToHsl(c) {
267
+ const maxChannel = (0, import_tsl3.max)(c.r, (0, import_tsl3.max)(c.g, c.b));
268
+ const minChannel = (0, import_tsl3.min)(c.r, (0, import_tsl3.min)(c.g, c.b));
269
+ const lightness = maxChannel.add(minChannel).mul(0.5);
270
+ const chroma = maxChannel.sub(minChannel);
271
+ const saturation = chroma.div((0, import_tsl3.abs)(lightness.mul(2).sub(1)).oneMinus().add(EPSILON));
272
+ return (0, import_tsl3.vec3)(gammaRgbHue(c), saturation, lightness);
273
+ }
274
+ function hslToGammaRgb(hsl) {
275
+ const hue = hsl.x;
276
+ const saturation = hsl.y;
277
+ const lightness = hsl.z;
278
+ const chroma = (0, import_tsl3.abs)(lightness.mul(2).sub(1)).oneMinus().mul(saturation);
279
+ const ramp = (0, import_tsl3.abs)(
280
+ (0, import_tsl3.fract)((0, import_tsl3.vec3)(hue).add((0, import_tsl3.vec3)(1, 2 / 3, 1 / 3))).mul(6).sub((0, import_tsl3.vec3)(3))
281
+ );
282
+ const hueRgb = (0, import_tsl3.clamp)(ramp.sub((0, import_tsl3.vec3)(1)), 0, 1);
283
+ return hueRgb.sub(0.5).mul(chroma).add(lightness);
284
+ }
285
+ var hslSpace = {
286
+ // Clamp into sRGB before the gamma transfer: HSL is an sRGB-gamut concept, and
287
+ // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels
288
+ // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.
289
+ fromLinear: (rgb) => gammaRgbToHsl(linearToSrgb((0, import_tsl3.clamp)(rgb, 0, 1))),
290
+ toLinear: (hsl) => srgbToLinear(hslToGammaRgb(hsl)),
291
+ lerp: (a, b, t, hue) => (0, import_tsl3.vec3)(hue(a.x, b.x, t, 1), (0, import_tsl3.mix)(a.y, b.y, t), (0, import_tsl3.mix)(a.z, b.z, t))
292
+ };
293
+
294
+ // src/primitives/color-space/hsv.ts
295
+ var import_tsl4 = require("three/tsl");
296
+ var EPSILON2 = 1e-10;
297
+ function gammaRgbToHsv(c) {
298
+ const p = (0, import_tsl4.mix)((0, import_tsl4.vec4)(c.b, c.g, -1 / 3, 2 / 3), (0, import_tsl4.vec4)(c.g, c.b, 0, -1 / 3), (0, import_tsl4.step)(c.b, c.g));
299
+ const q = (0, import_tsl4.mix)((0, import_tsl4.vec4)(p.x, p.y, p.w, c.r), (0, import_tsl4.vec4)(c.r, p.y, p.z, p.x), (0, import_tsl4.step)(p.x, c.r));
300
+ const chroma = q.x.sub((0, import_tsl4.min)(q.w, q.y));
301
+ const hue = (0, import_tsl4.abs)(q.z.add(q.w.sub(q.y).div(chroma.mul(6).add(EPSILON2))));
302
+ const saturation = chroma.div(q.x.add(EPSILON2));
303
+ return (0, import_tsl4.vec3)(hue, saturation, q.x);
304
+ }
305
+ function hsvToGammaRgb(hsv) {
306
+ const hue = hsv.x;
307
+ const saturation = hsv.y;
308
+ const value = hsv.z;
309
+ const ramp = (0, import_tsl4.abs)(
310
+ (0, import_tsl4.fract)((0, import_tsl4.vec3)(hue).add((0, import_tsl4.vec3)(1, 2 / 3, 1 / 3))).mul(6).sub((0, import_tsl4.vec3)(3))
311
+ );
312
+ return (0, import_tsl4.mix)((0, import_tsl4.vec3)(1), (0, import_tsl4.clamp)(ramp.sub((0, import_tsl4.vec3)(1)), 0, 1), saturation).mul(value);
313
+ }
314
+ var hsvSpace = {
315
+ // Clamp into sRGB before the gamma transfer: HSV is an sRGB-gamut concept, and
316
+ // the sRGB OETF's pow() can't be WGSL const-evaluated on the negative channels
317
+ // of an out-of-sRGB (wide-gamut) stop color — that crashed the shader compile.
318
+ fromLinear: (rgb) => gammaRgbToHsv(linearToSrgb((0, import_tsl4.clamp)(rgb, 0, 1))),
319
+ toLinear: (hsv) => srgbToLinear(hsvToGammaRgb(hsv)),
320
+ lerp: (a, b, t, hue) => (0, import_tsl4.vec3)(hue(a.x, b.x, t, 1), (0, import_tsl4.mix)(a.y, b.y, t), (0, import_tsl4.mix)(a.z, b.z, t))
321
+ };
322
+
323
+ // src/primitives/color-space/lch.ts
324
+ var import_tsl5 = require("three/tsl");
325
+ var TWO_PI = Math.PI * 2;
326
+ var WHITE_X = 0.95047;
327
+ var WHITE_Y = 1;
328
+ var WHITE_Z = 1.08883;
329
+ var EPSILON3 = 216 / 24389;
330
+ var KAPPA = 24389 / 27;
331
+ function labForward(ratio) {
332
+ const linearPart = ratio.mul(KAPPA).add(16).div(116);
333
+ const cubeRootPart = (0, import_tsl5.cbrt)(ratio);
334
+ return (0, import_tsl5.mix)(linearPart, cubeRootPart, (0, import_tsl5.step)(EPSILON3, ratio));
335
+ }
336
+ function labInverse(f) {
337
+ const cubed = f.mul(f).mul(f);
338
+ const linearPart = f.mul(116).sub(16).div(KAPPA);
339
+ return (0, import_tsl5.mix)(linearPart, cubed, (0, import_tsl5.step)(EPSILON3, cubed));
340
+ }
341
+ function linearToLch(rgb) {
342
+ const r = rgb.r;
343
+ const g = rgb.g;
344
+ const b = rgb.b;
345
+ const x = r.mul(0.4123907993).add(g.mul(0.3575843394)).add(b.mul(0.1804807884));
346
+ const y = r.mul(0.2126390059).add(g.mul(0.7151686788)).add(b.mul(0.0721923154));
347
+ const z = r.mul(0.0193308187).add(g.mul(0.1191947798)).add(b.mul(0.9505321522));
348
+ const fx = labForward(x.div(WHITE_X));
349
+ const fy = labForward(y.div(WHITE_Y));
350
+ const fz = labForward(z.div(WHITE_Z));
351
+ const lightness = fy.mul(116).sub(16);
352
+ const greenRed = fx.sub(fy).mul(500);
353
+ const blueYellow = fy.sub(fz).mul(200);
354
+ const chroma = (0, import_tsl5.length)((0, import_tsl5.vec2)(greenRed, blueYellow));
355
+ const hue = (0, import_tsl5.atan2)(blueYellow, greenRed);
356
+ return (0, import_tsl5.vec3)(lightness, chroma, hue);
357
+ }
358
+ function lchToLinear(lch) {
359
+ const lightness = lch.x;
360
+ const chroma = lch.y;
361
+ const hue = lch.z;
362
+ const greenRed = chroma.mul((0, import_tsl5.cos)(hue));
363
+ const blueYellow = chroma.mul((0, import_tsl5.sin)(hue));
364
+ const fy = lightness.add(16).div(116);
365
+ const fx = fy.add(greenRed.div(500));
366
+ const fz = fy.sub(blueYellow.div(200));
367
+ const x = labInverse(fx).mul(WHITE_X);
368
+ const y = labInverse(fy).mul(WHITE_Y);
369
+ const z = labInverse(fz).mul(WHITE_Z);
370
+ const r = x.mul(3.2409699419).sub(y.mul(1.5373831776)).sub(z.mul(0.4986107603));
371
+ const g = x.mul(-0.9692436363).add(y.mul(1.8759675015)).add(z.mul(0.0415550574));
372
+ const b = x.mul(0.0556300797).sub(y.mul(0.2039769589)).add(z.mul(1.0569715142));
373
+ return (0, import_tsl5.vec3)(r, g, b);
374
+ }
375
+ var lchSpace = {
376
+ fromLinear: linearToLch,
377
+ toLinear: lchToLinear,
378
+ lerp: (a, b, t, hue) => (0, import_tsl5.vec3)((0, import_tsl5.mix)(a.x, b.x, t), (0, import_tsl5.mix)(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI))
379
+ };
380
+
381
+ // src/primitives/color-space/linear.ts
382
+ var import_tsl6 = require("three/tsl");
383
+ var linearSpace = {
384
+ fromLinear: (rgb) => (0, import_tsl6.vec3)(rgb),
385
+ toLinear: (coords) => (0, import_tsl6.vec3)(coords),
386
+ lerp: (a, b, t) => (0, import_tsl6.mix)(a, b, t)
387
+ };
388
+
389
+ // src/primitives/color-space/oklab.ts
390
+ var import_tsl7 = require("three/tsl");
391
+ function linearToOklab(rgb) {
392
+ const r = rgb.r;
393
+ const g = rgb.g;
394
+ const b = rgb.b;
395
+ const longCone = r.mul(0.4122214708).add(g.mul(0.5363325363)).add(b.mul(0.0514459929));
396
+ const mediumCone = r.mul(0.2119034982).add(g.mul(0.6806995451)).add(b.mul(0.1073969566));
397
+ const shortCone = r.mul(0.0883024619).add(g.mul(0.2817188376)).add(b.mul(0.6299787005));
398
+ const longRoot = (0, import_tsl7.cbrt)(longCone);
399
+ const mediumRoot = (0, import_tsl7.cbrt)(mediumCone);
400
+ const shortRoot = (0, import_tsl7.cbrt)(shortCone);
401
+ const lightness = longRoot.mul(0.2104542553).add(mediumRoot.mul(0.793617785)).sub(shortRoot.mul(0.0040720468));
402
+ const greenRed = longRoot.mul(1.9779984951).sub(mediumRoot.mul(2.428592205)).add(shortRoot.mul(0.4505937099));
403
+ const blueYellow = longRoot.mul(0.0259040371).add(mediumRoot.mul(0.7827717662)).sub(shortRoot.mul(0.808675766));
404
+ return (0, import_tsl7.vec3)(lightness, greenRed, blueYellow);
405
+ }
406
+ function oklabToLinear(lab) {
407
+ const lightness = lab.x;
408
+ const greenRed = lab.y;
409
+ const blueYellow = lab.z;
410
+ const longRoot = lightness.add(greenRed.mul(0.3963377774)).add(blueYellow.mul(0.2158037573));
411
+ const mediumRoot = lightness.sub(greenRed.mul(0.1055613458)).sub(blueYellow.mul(0.0638541728));
412
+ const shortRoot = lightness.sub(greenRed.mul(0.0894841775)).sub(blueYellow.mul(1.291485548));
413
+ const longCone = longRoot.mul(longRoot).mul(longRoot);
414
+ const mediumCone = mediumRoot.mul(mediumRoot).mul(mediumRoot);
415
+ const shortCone = shortRoot.mul(shortRoot).mul(shortRoot);
416
+ const r = longCone.mul(4.0767416621).sub(mediumCone.mul(3.3077115913)).add(shortCone.mul(0.2309699292));
417
+ const g = longCone.mul(-1.2684380046).add(mediumCone.mul(2.6097574011)).sub(shortCone.mul(0.3413193965));
418
+ const b = longCone.mul(-0.0041960863).sub(mediumCone.mul(0.7034186147)).add(shortCone.mul(1.707614701));
419
+ return (0, import_tsl7.vec3)(r, g, b);
420
+ }
421
+ var oklabSpace = {
422
+ fromLinear: linearToOklab,
423
+ toLinear: oklabToLinear,
424
+ lerp: (a, b, t) => (0, import_tsl7.mix)(a, b, t)
425
+ };
426
+
427
+ // src/primitives/color-space/oklch.ts
428
+ var import_tsl8 = require("three/tsl");
429
+ var TWO_PI2 = Math.PI * 2;
430
+ function linearToOklch(rgb) {
431
+ const lab = linearToOklab(rgb);
432
+ const lightness = lab.x;
433
+ const greenRed = lab.y;
434
+ const blueYellow = lab.z;
435
+ const chroma = (0, import_tsl8.length)((0, import_tsl8.vec2)(greenRed, blueYellow));
436
+ const hue = (0, import_tsl8.atan2)(blueYellow, greenRed);
437
+ return (0, import_tsl8.vec3)(lightness, chroma, hue);
438
+ }
439
+ function oklchToLinear(lch) {
440
+ const lightness = lch.x;
441
+ const chroma = lch.y;
442
+ const hue = lch.z;
443
+ const greenRed = chroma.mul((0, import_tsl8.cos)(hue));
444
+ const blueYellow = chroma.mul((0, import_tsl8.sin)(hue));
445
+ return oklabToLinear((0, import_tsl8.vec3)(lightness, greenRed, blueYellow));
446
+ }
447
+ var oklchSpace = {
448
+ fromLinear: linearToOklch,
449
+ toLinear: oklchToLinear,
450
+ lerp: (a, b, t, hue) => (0, import_tsl8.vec3)((0, import_tsl8.mix)(a.x, b.x, t), (0, import_tsl8.mix)(a.y, b.y, t), hue(a.z, b.z, t, TWO_PI2))
451
+ };
452
+
453
+ // src/primitives/color-space/registry.ts
454
+ var colorSpaces = {
455
+ linear: linearSpace,
456
+ oklab: oklabSpace,
457
+ oklch: oklchSpace,
458
+ lch: lchSpace,
459
+ hsl: hslSpace,
460
+ hsv: hsvSpace
461
+ };
462
+
463
+ // src/primitives/color-ramp/color-ramp.ts
464
+ function colorRamp(t, stops, colorSpace = "linear", hueInterpolation = "shorter") {
465
+ const space = colorSpaces[colorSpace];
466
+ const hue = hueArcInterpolators[hueInterpolation];
164
467
  const first = stops[0];
165
- if (first === void 0) return (0, import_tsl.vec3)(0, 0, 0);
166
- if (stops.length === 1) return (0, import_tsl.mix)(first.color, first.color, 0);
167
- let result = (0, import_tsl.mix)(first.color, first.color, 0);
468
+ if (first === void 0) return (0, import_tsl9.vec3)(0, 0, 0);
469
+ const firstCoords = space.fromLinear((0, import_tsl9.vec3)(first.color));
470
+ if (stops.length === 1) return space.toLinear(firstCoords);
471
+ let resultCoords = firstCoords;
168
472
  for (let i = 1; i < stops.length; i += 1) {
169
473
  const previousStop = stops[i - 1];
170
474
  const next = stops[i];
171
475
  if (previousStop === void 0 || next === void 0) continue;
172
476
  const positionSpan = next.position - previousStop.position;
173
477
  if (positionSpan <= 0) continue;
174
- const localT = (0, import_tsl2.clamp)((0, import_tsl2.div)((0, import_tsl2.sub)(t, previousStop.position), positionSpan), 0, 1);
175
- result = (0, import_tsl.mix)(result, next.color, localT);
478
+ const localT = (0, import_tsl9.clamp)((0, import_tsl9.div)((0, import_tsl9.sub)(t, previousStop.position), positionSpan), 0, 1);
479
+ const nextCoords = space.fromLinear((0, import_tsl9.vec3)(next.color));
480
+ resultCoords = space.lerp(resultCoords, nextCoords, localT, hue);
176
481
  }
177
- return result;
482
+ return space.toLinear(resultCoords);
483
+ }
484
+
485
+ // src/primitives/color-space/mix-color.ts
486
+ var import_tsl10 = require("three/tsl");
487
+ function mixColor(colorA, colorB, t, colorSpace = "oklab", hueInterpolation = "shorter") {
488
+ const space = colorSpaces[colorSpace];
489
+ const hue = hueArcInterpolators[hueInterpolation];
490
+ const a = space.fromLinear((0, import_tsl10.vec3)(colorA));
491
+ const b = space.fromLinear((0, import_tsl10.vec3)(colorB));
492
+ return space.toLinear(space.lerp(a, b, t, hue));
493
+ }
494
+
495
+ // src/primitives/color-space/cpu-convert.ts
496
+ function oklabToLinearSrgb(lightness, greenRed, blueYellow) {
497
+ const longRoot = lightness + 0.3963377774 * greenRed + 0.2158037573 * blueYellow;
498
+ const mediumRoot = lightness - 0.1055613458 * greenRed - 0.0638541728 * blueYellow;
499
+ const shortRoot = lightness - 0.0894841775 * greenRed - 1.291485548 * blueYellow;
500
+ const longCone = longRoot * longRoot * longRoot;
501
+ const mediumCone = mediumRoot * mediumRoot * mediumRoot;
502
+ const shortCone = shortRoot * shortRoot * shortRoot;
503
+ const red = 4.0767416621 * longCone - 3.3077115913 * mediumCone + 0.2309699292 * shortCone;
504
+ const green = -1.2684380046 * longCone + 2.6097574011 * mediumCone - 0.3413193965 * shortCone;
505
+ const blue = -0.0041960863 * longCone - 0.7034186147 * mediumCone + 1.707614701 * shortCone;
506
+ return [red, green, blue];
507
+ }
508
+ function oklchToLinearSrgb(lightness, chroma, hueDegrees) {
509
+ const hueRadians = hueDegrees * Math.PI / 180;
510
+ const greenRed = chroma * Math.cos(hueRadians);
511
+ const blueYellow = chroma * Math.sin(hueRadians);
512
+ return oklabToLinearSrgb(lightness, greenRed, blueYellow);
513
+ }
514
+ function parseComponent(token, scale) {
515
+ const trimmed = token.trim();
516
+ if (trimmed.endsWith("%")) {
517
+ return parseFloat(trimmed.slice(0, -1)) / 100 * scale;
518
+ }
519
+ return parseFloat(trimmed);
520
+ }
521
+ function functionArgs(input, prefix) {
522
+ const inner = input.slice(prefix.length, input.lastIndexOf(")"));
523
+ const beforeAlpha = inner.split("/")[0] ?? "";
524
+ return beforeAlpha.trim().split(/[\s,]+/).filter((token) => token.length > 0);
525
+ }
526
+ function parseColorString(input) {
527
+ const value = input.trim();
528
+ if (value.startsWith("#")) {
529
+ const hex = value.slice(1);
530
+ return [
531
+ srgbChannelToLinear(parseInt(hex.slice(0, 2), 16) / 255),
532
+ srgbChannelToLinear(parseInt(hex.slice(2, 4), 16) / 255),
533
+ srgbChannelToLinear(parseInt(hex.slice(4, 6), 16) / 255)
534
+ ];
535
+ }
536
+ if (value.startsWith("oklch(")) {
537
+ const [lightnessToken, chromaToken, hueToken] = functionArgs(value, "oklch(");
538
+ if (lightnessToken === void 0 || chromaToken === void 0 || hueToken === void 0) {
539
+ throw new Error(`Invalid oklch() color: "${input}"`);
540
+ }
541
+ const lightness = parseComponent(lightnessToken, 1);
542
+ const chroma = parseComponent(chromaToken, 0.4);
543
+ const hueDegrees = parseFloat(hueToken.replace(/deg$/, ""));
544
+ return oklchToLinearSrgb(lightness, chroma, hueDegrees);
545
+ }
546
+ if (value.startsWith("oklab(")) {
547
+ const [lightnessToken, aToken, bToken] = functionArgs(value, "oklab(");
548
+ if (lightnessToken === void 0 || aToken === void 0 || bToken === void 0) {
549
+ throw new Error(`Invalid oklab() color: "${input}"`);
550
+ }
551
+ return oklabToLinearSrgb(
552
+ parseComponent(lightnessToken, 1),
553
+ parseComponent(aToken, 0.4),
554
+ parseComponent(bToken, 0.4)
555
+ );
556
+ }
557
+ throw new Error(`Unsupported color syntax: "${input}". Use #rrggbb, oklch(...), or oklab(...).`);
178
558
  }
179
559
 
180
560
  // src/primitives/noise/noise.ts
181
- var import_tsl3 = require("three/tsl");
561
+ var import_tsl11 = require("three/tsl");
182
562
  function simplexNoise(p) {
183
- return (0, import_tsl3.mx_noise_float)(p);
563
+ return (0, import_tsl11.mx_noise_float)(p);
184
564
  }
185
565
 
186
566
  // src/primitives/fbm/fbm.ts
187
- var import_tsl4 = require("three/tsl");
567
+ var import_tsl12 = require("three/tsl");
188
568
  function fractalNoise(p, opts = {}) {
189
569
  const octaves = opts.octaves ?? 4;
190
570
  const lacunarity = opts.lacunarity ?? 2;
@@ -197,7 +577,7 @@ function fractalNoise(p, opts = {}) {
197
577
  frequency *= lacunarity;
198
578
  amplitude *= gain;
199
579
  total += amplitude;
200
- const pAtFreq = (0, import_tsl4.add)((0, import_tsl4.mul)(p, frequency), i * 100);
580
+ const pAtFreq = (0, import_tsl12.add)((0, import_tsl12.mul)(p, frequency), i * 100);
201
581
  const layer = simplexNoise(pAtFreq).mul(amplitude);
202
582
  sum = sum.add(layer);
203
583
  }
@@ -205,9 +585,9 @@ function fractalNoise(p, opts = {}) {
205
585
  }
206
586
 
207
587
  // src/primitives/voronoi/voronoi.ts
208
- var import_tsl5 = require("three/tsl");
588
+ var import_tsl13 = require("three/tsl");
209
589
  function voronoi(p) {
210
- return (0, import_tsl5.mx_worley_noise_float)(p);
590
+ return (0, import_tsl13.mx_worley_noise_float)(p);
211
591
  }
212
592
 
213
593
  // src/primitives/quantize/quantize.ts
@@ -220,25 +600,25 @@ function quantize(t, steps) {
220
600
  }
221
601
 
222
602
  // src/primitives/sdf-circle/sdf-circle.ts
223
- var import_tsl6 = require("three/tsl");
603
+ var import_tsl14 = require("three/tsl");
224
604
  function signedDistanceFieldCircle(p, radius) {
225
- return (0, import_tsl6.length)(p).sub(radius);
605
+ return (0, import_tsl14.length)(p).sub(radius);
226
606
  }
227
607
 
228
608
  // src/primitives/displace/displace.ts
229
- var import_tsl7 = require("three/tsl");
609
+ var import_tsl15 = require("three/tsl");
230
610
  function displace(p, by) {
231
- return (0, import_tsl7.add)(p, by);
611
+ return (0, import_tsl15.add)(p, by);
232
612
  }
233
613
 
234
614
  // src/primitives/cursor-ripple/cursor-ripple.ts
235
- var import_tsl10 = require("three/tsl");
615
+ var import_tsl18 = require("three/tsl");
236
616
 
237
617
  // src/primitives/time/time.ts
238
- var import_tsl9 = require("three/tsl");
618
+ var import_tsl17 = require("three/tsl");
239
619
 
240
620
  // src/runtime/reduced-motion/reduced-motion.ts
241
- var import_tsl8 = require("three/tsl");
621
+ var import_tsl16 = require("three/tsl");
242
622
  var state = {
243
623
  policy: "auto",
244
624
  watchers: /* @__PURE__ */ new Set()
@@ -315,7 +695,7 @@ var globalWatcher = null;
315
695
  function getReducedMotionTimeScale() {
316
696
  if (globalScaleUniform === null) {
317
697
  globalWatcher = createReducedMotionWatcher();
318
- globalScaleUniform = (0, import_tsl8.uniform)(globalWatcher.scale());
698
+ globalScaleUniform = (0, import_tsl16.uniform)(globalWatcher.scale());
319
699
  globalWatcher.subscribe((s) => {
320
700
  if (globalScaleUniform === null) return;
321
701
  globalScaleUniform.value = s;
@@ -325,7 +705,7 @@ function getReducedMotionTimeScale() {
325
705
  }
326
706
 
327
707
  // src/primitives/time/time.ts
328
- var elapsedTime = import_tsl9.time.mul(getReducedMotionTimeScale());
708
+ var elapsedTime = import_tsl17.time.mul(getReducedMotionTimeScale());
329
709
 
330
710
  // src/primitives/cursor-ripple/cursor-ripple.ts
331
711
  function cursorRipple(p, center, opts = {}) {
@@ -333,18 +713,31 @@ function cursorRipple(p, center, opts = {}) {
333
713
  const frequency = opts.frequency ?? 30;
334
714
  const speed = opts.speed ?? 6;
335
715
  const amplitude = opts.amplitude ?? 0.5;
336
- const d = (0, import_tsl10.length)((0, import_tsl10.sub)(p, center));
337
- const wave = (0, import_tsl10.sin)(d.mul(frequency).sub(elapsedTime.mul(speed)));
338
- const decay = (0, import_tsl10.smoothstep)(reach, 0, d);
716
+ const d = (0, import_tsl18.length)((0, import_tsl18.sub)(p, center));
717
+ const wave = (0, import_tsl18.sin)(d.mul(frequency).sub(elapsedTime.mul(speed)));
718
+ const decay = (0, import_tsl18.smoothstep)(reach, 0, d);
339
719
  return wave.mul(amplitude).mul(decay);
340
720
  }
341
721
 
342
722
  // src/primitives/film-grain/film-grain.ts
343
- var import_tsl11 = require("three/tsl");
723
+ var import_tsl19 = require("three/tsl");
344
724
  function filmGrain(intensity, timeOffset = 0) {
345
- const pixel = import_tsl11.screenCoordinate.xy.floor();
346
- const seed = pixel.x.toUint().mul(1973).add(pixel.y.toUint().mul(9277)).add((0, import_tsl11.mul)(timeOffset, 26699).toUint());
347
- return (0, import_tsl11.hash)(seed).sub(0.5).mul(intensity);
725
+ const pixel = import_tsl19.screenCoordinate.xy.floor();
726
+ const seed = pixel.x.toUint().mul(1973).add(pixel.y.toUint().mul(9277)).add((0, import_tsl19.mul)(timeOffset, 26699).toUint());
727
+ return (0, import_tsl19.hash)(seed).sub(0.5).mul(intensity);
728
+ }
729
+
730
+ // src/primitives/dither/dither.ts
731
+ var import_tsl20 = require("three/tsl");
732
+ function hash21(coord) {
733
+ return (0, import_tsl20.fract)((0, import_tsl20.sin)((0, import_tsl20.dot)(coord, (0, import_tsl20.vec2)(12.9898, 78.233))).mul(43758.5453));
734
+ }
735
+ function dither(color, coord, amount = 1 / 255) {
736
+ const pixelCoord = (0, import_tsl20.vec2)(coord);
737
+ const firstHash = hash21(pixelCoord);
738
+ const secondHash = hash21(pixelCoord.add((0, import_tsl20.vec2)(0.5, 0.5)));
739
+ const triangularNoise = firstHash.sub(secondHash).mul(0.5);
740
+ return (0, import_tsl20.vec3)(color).add(triangularNoise.mul(amount));
348
741
  }
349
742
 
350
743
  // src/runtime/visibility/visibility.ts
@@ -558,15 +951,21 @@ var FrameScheduler = class {
558
951
  createVisibilityWatcher,
559
952
  cursorRipple,
560
953
  displace,
954
+ dither,
561
955
  elapsedTime,
562
956
  filmGrain,
563
957
  fractalNoise,
564
958
  getReducedMotionPolicy,
565
959
  getReducedMotionTimeScale,
960
+ mixColor,
961
+ oklabToLinearSrgb,
962
+ oklchToLinearSrgb,
963
+ parseColorString,
566
964
  quantize,
567
965
  setReducedMotionPolicy,
568
966
  signedDistanceFieldCircle,
569
967
  simplexNoise,
968
+ srgbChannelToLinear,
570
969
  voronoi
571
970
  });
572
971
  //# sourceMappingURL=index.cjs.map