palette-shader 0.12.0 → 0.13.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/README.md CHANGED
@@ -35,7 +35,10 @@ import { PaletteViz } from 'palette-shader';
35
35
  import { converter } from 'culori';
36
36
 
37
37
  const toSRGB = converter('srgb');
38
- const toRGB = (hex) => { const c = toSRGB(hex); return [c.r, c.g, c.b]; };
38
+ const toRGB = (hex) => {
39
+ const c = toSRGB(hex);
40
+ return [c.r, c.g, c.b];
41
+ };
39
42
 
40
43
  // option A — pass a container, canvas is appended automatically
41
44
  const viz = new PaletteViz({
@@ -50,6 +53,8 @@ const viz = new PaletteViz({ palette: ['#264653', '#2a9d8f', '#e9c46a'].map(toRG
50
53
  document.querySelector('#app').appendChild(viz.canvas);
51
54
  ```
52
55
 
56
+ If you only use the 2D renderer, import `PaletteViz` and let your bundler tree-shake the rest. `PaletteViz3D` pulls in substantially more code for mesh generation, extra shaders, and interaction handling, so it is worth avoiding in apps that never render the 3-D view.
57
+
53
58
  ---
54
59
 
55
60
  ## Constructor
@@ -60,20 +65,21 @@ new PaletteViz(options?: PaletteVizOptions)
60
65
 
61
66
  All options are optional. The palette defaults to a random 20-color set.
62
67
 
63
- | Option | Type | Default | Description |
64
- | ---------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
65
- | `palette` | `[number, number, number][]` | random | sRGB colors as `[r, g, b]` arrays, each component in the `0–1` range |
66
- | `container` | `HTMLElement` | `undefined` | Element the canvas is appended to. Omit and use `viz.canvas` to place it yourself |
67
- | `width` | `number` | `512` | Canvas width in CSS pixels |
68
- | `height` | `number` | `512` | Canvas height in CSS pixels |
69
- | `pixelRatio` | `number` | `devicePixelRatio` | Renderer pixel ratio |
70
- | `colorModel` | `string` | `'okhsv'` | Color space for the visualization (see [Color models](#color-models)) |
71
- | `distanceMetric` | `string` | `'oklab'` | Distance function for nearest-color matching (see [Distance metrics](#distance-metrics)) |
72
- | `axis` | `'x' \| 'y' \| 'z'` | `'y'` | Which axis the `position` value controls |
73
- | `position` | `number` | `0` | 0–1 position along the chosen axis |
74
- | `invertZ` | `boolean` | `false` | Flip the lightness/value axis |
75
- | `showRaw` | `boolean` | `false` | Bypass nearest-color matching (shows the raw color space) |
76
- | `outlineWidth` | `number` | `0` | Draw a transparent outline where palette regions meet. Width in physical pixels. `0` disables (no overhead). |
68
+ | Option | Type | Default | Description |
69
+ | ---------------- | ---------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
70
+ | `palette` | `[number, number, number][]` | random | sRGB colors as `[r, g, b]` arrays, each component in the `0–1` range |
71
+ | `container` | `HTMLElement` | `undefined` | Element the canvas is appended to. Omit and use `viz.canvas` to place it yourself |
72
+ | `width` | `number` | `512` | Canvas width in CSS pixels |
73
+ | `height` | `number` | `512` | Canvas height in CSS pixels |
74
+ | `pixelRatio` | `number` | `devicePixelRatio` | Renderer pixel ratio |
75
+ | `colorModel` | `string` | `'okhsv'` | Color space for the visualization (see [Color models](#color-models)) |
76
+ | `distanceMetric` | `string` | `'oklab'` | Distance function for nearest-color matching (see [Distance metrics](#distance-metrics)) |
77
+ | `axis` | `'x' \| 'y' \| 'z'` | `'y'` | Which axis the `position` value controls |
78
+ | `position` | `number` | `0` | 0–1 position along the chosen axis |
79
+ | `invertZ` | `boolean` | `false` | Flip the lightness/value axis |
80
+ | `showRaw` | `boolean` | `false` | Bypass nearest-color matching (shows the raw color space) |
81
+ | `outlineWidth` | `number` | `0` | Draw a transparent outline where palette regions meet. Width in physical pixels. `0` disables (no overhead). |
82
+ | `gamutClip` | `boolean` | `false` | Discard out-of-sRGB-gamut pixels instead of clamping. Reveals the true gamut boundary of the color model. |
77
83
 
78
84
  ---
79
85
 
@@ -82,13 +88,18 @@ All options are optional. The palette defaults to a random 20-color set.
82
88
  Every constructor option is also a live setter/getter. Assigning any of them re-renders immediately via `requestAnimationFrame`.
83
89
 
84
90
  ```js
85
- viz.palette = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
91
+ viz.palette = [
92
+ [1, 0, 0],
93
+ [0, 1, 0],
94
+ [0, 0, 1],
95
+ ];
86
96
  viz.position = 0.5;
87
97
  viz.colorModel = 'okhslPolar';
88
98
  viz.distanceMetric = 'deltaE2000';
89
99
  viz.invertZ = true;
90
100
  viz.showRaw = true;
91
101
  viz.outlineWidth = 2; // transparent border between regions, in physical pixels
102
+ viz.gamutClip = true; // discard out-of-gamut pixels
92
103
  viz.pixelRatio = window.devicePixelRatio; // update after display changes
93
104
  ```
94
105
 
@@ -178,11 +189,11 @@ Controls the 3-D color space the visualization is rendered in. Polar variants (`
178
189
 
179
190
  **CIE Lab / LCH — D65**
180
191
 
181
- | Value | Shape | Description |
182
- | --------------- | ----- | --------------------------------------------------------------- |
183
- | `'cielab'` | cube | CIELab D65: x→a, y→b, z→L. The classic perceptual color space. |
184
- | `'cielch'` | cube | CIELab D65 in cylindrical LCH coordinates. |
185
- | `'cielchPolar'` | wheel | Polar form of CIELch D65. |
192
+ | Value | Shape | Description |
193
+ | --------------- | ----- | -------------------------------------------------------------- |
194
+ | `'cielab'` | cube | CIELab D65: x→a, y→b, z→L. The classic perceptual color space. |
195
+ | `'cielch'` | cube | CIELab D65 in cylindrical LCH coordinates. |
196
+ | `'cielchPolar'` | wheel | Polar form of CIELch D65. |
186
197
 
187
198
  **CIE Lab / LCH — D50**
188
199
 
@@ -313,7 +324,11 @@ import { paletteToRGBA, randomPalette, fragmentShader } from 'palette-shader';
313
324
 
314
325
  // Get raw RGBA bytes (Uint8Array, sRGB, 4 bytes per color)
315
326
  // Useful for building your own WebGL texture or processing palette data
316
- const rgba = paletteToRGBA([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
327
+ const rgba = paletteToRGBA([
328
+ [1, 0, 0],
329
+ [0, 1, 0],
330
+ [0, 0, 1],
331
+ ]);
317
332
 
318
333
  // Quick random palette for prototyping
319
334
  const palette = randomPalette(16);
@@ -337,7 +352,7 @@ const viz3d = new PaletteViz3D({
337
352
  palette: ['#264653', '#2a9d8f', '#e9c46a', '#f4a261', '#e76f51'].map(toRGB),
338
353
  container: document.querySelector('#app'),
339
354
  colorModel: 'okhsv',
340
- position: 1.0, // 1 = full volume, 0 = fully sliced
355
+ position: 1.0, // 1 = full volume, 0 = fully sliced
341
356
  outlineWidth: 2,
342
357
  });
343
358
  ```
@@ -350,7 +365,7 @@ new PaletteViz3D(options?: PaletteViz3DOptions)
350
365
 
351
366
  | Option | Type | Default | Description |
352
367
  | ---------------- | ---------------------------- | --------------------- | ---------------------------------------------------------------------------- |
353
- | `palette` | `[number, number, number][]` | random | sRGB colors as `[r, g, b]`, each in `0–1` |
368
+ | `palette` | `[number, number, number][]` | random | sRGB colors as `[r, g, b]`, each in `0–1` |
354
369
  | `container` | `HTMLElement` | `undefined` | Element the canvas is appended to |
355
370
  | `width` | `number` | `512` | Canvas width in CSS pixels |
356
371
  | `height` | `number` | `512` | Canvas height in CSS pixels |
@@ -361,7 +376,8 @@ new PaletteViz3D(options?: PaletteViz3DOptions)
361
376
  | `invertZ` | `boolean` | `false` | Flip the lightness/value axis |
362
377
  | `showRaw` | `boolean` | `false` | Bypass nearest-color matching |
363
378
  | `outlineWidth` | `number` | `0` | Transparent outline width (physical px). `0` disables |
364
- | `modelMatrix` | `Float32Array` | slight tilt (default) | Initial 4×4 column-major model rotation matrix |
379
+ | `gamutClip` | `boolean` | `false` | Discard out-of-sRGB-gamut pixels instead of clamping |
380
+ | `modelMatrix` | `Float32Array` | slight tilt (default) | Initial 4×4 column-major model rotation matrix |
365
381
 
366
382
  ### Properties (3D)
367
383
 
@@ -369,13 +385,39 @@ All constructor options except `modelMatrix` are live setter/getters (re-render
369
385
 
370
386
  Additional properties:
371
387
 
372
- | Property | Type | Description |
373
- | ------------- | -------------- | --------------------------------------------------------------- |
374
- | `canvas` | `HTMLCanvasElement` | The canvas (read-only) |
375
- | `modelMatrix` | `Float32Array` | Get/set the 4×4 model rotation matrix (copies on read & write) |
388
+ | Property | Type | Description |
389
+ | ------------- | ------------------- | -------------------------------------------------------------- |
390
+ | `canvas` | `HTMLCanvasElement` | The canvas (read-only) |
391
+ | `modelMatrix` | `Float32Array` | Get/set the 4×4 model rotation matrix (copies on read & write) |
376
392
 
377
393
  ### Methods (3D)
378
394
 
395
+ #### `getColorAtUV(x, y)`
396
+
397
+ Returns the rendered color at normalised screen coordinates (`0–1` on both axes, y=0 is top) as `[r, g, b]` in `0–1` sRGB, or `null` if the cursor is over a transparent pixel (i.e. outside the 3D geometry). Flushes any pending rAF frame so the reading is always current.
398
+
399
+ ```js
400
+ canvas.addEventListener('mousemove', (e) => {
401
+ const rect = canvas.getBoundingClientRect();
402
+ const color = viz3d.getColorAtUV(
403
+ (e.clientX - rect.left) / rect.width,
404
+ (e.clientY - rect.top) / rect.height,
405
+ );
406
+ if (color) {
407
+ const hex =
408
+ '#' +
409
+ color
410
+ .map((c) =>
411
+ Math.round(c * 255)
412
+ .toString(16)
413
+ .padStart(2, '0'),
414
+ )
415
+ .join('');
416
+ console.log(hex);
417
+ }
418
+ });
419
+ ```
420
+
379
421
  #### `rotate(dx, dy)`
380
422
 
381
423
  Apply an incremental trackball rotation. `dx` and `dy` are in radians (screen-space). Left-multiplies incremental X/Y rotations onto the accumulated model matrix.
@@ -413,13 +455,13 @@ const model = mat4Multiply(mat4RotateX(0.4), mat4RotateY(0.6));
413
455
  viz3d.modelMatrix = model;
414
456
  ```
415
457
 
416
- | Function | Signature | Description |
417
- | ---------------- | ------------------------------------------------------ | ------------------------------------- |
418
- | `mat4Perspective`| `(fov, aspect, near, far) → Float32Array` | Perspective projection matrix |
419
- | `mat4Multiply` | `(a, b) → Float32Array` | Matrix multiplication `a × b` |
420
- | `mat4RotateX` | `(angle) → Float32Array` | Rotation around X axis (radians) |
421
- | `mat4RotateY` | `(angle) → Float32Array` | Rotation around Y axis (radians) |
422
- | `mat4Translate` | `(x, y, z) → Float32Array` | Translation matrix |
458
+ | Function | Signature | Description |
459
+ | ----------------- | ----------------------------------------- | -------------------------------- |
460
+ | `mat4Perspective` | `(fov, aspect, near, far) → Float32Array` | Perspective projection matrix |
461
+ | `mat4Multiply` | `(a, b) → Float32Array` | Matrix multiplication `a × b` |
462
+ | `mat4RotateX` | `(angle) → Float32Array` | Rotation around X axis (radians) |
463
+ | `mat4RotateY` | `(angle) → Float32Array` | Rotation around Y axis (radians) |
464
+ | `mat4Translate` | `(x, y, z) → Float32Array` | Translation matrix |
423
465
 
424
466
  ---
425
467
 
@@ -63,6 +63,12 @@ export declare class PaletteViz {
63
63
  export declare class PaletteViz3D {
64
64
  #private;
65
65
  constructor({ palette, width, height, pixelRatio, container, colorModel, distanceMetric, invertZ, showRaw, outlineWidth, gamutClip, position, modelMatrix, }?: PaletteViz3DOptions);
66
+ /**
67
+ * Read the rendered colour at normalised screen coordinates (0–1, y=0 is top).
68
+ * Returns [r, g, b] in [0, 1], or null if the pixel is transparent (no geometry).
69
+ * Flushes any pending rAF frame to ensure the reading is up to date.
70
+ */
71
+ getColorAtUV(x: number, y: number): [number, number, number] | null;
66
72
  get canvas(): HTMLCanvasElement;
67
73
  get width(): number;
68
74
  get height(): number;