palette-shader 0.4.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/README.md +25 -1
- package/dist/palette-shader.d.ts +4 -1
- package/dist/palette-shader.js +186 -130
- package/dist/palette-shader.js.map +1 -1
- package/dist/palette-shader.umd.cjs +39 -15
- package/dist/palette-shader.umd.cjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +111 -2
- package/src/types.ts +1 -0
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ All options are optional. The palette defaults to a random 20-colour set.
|
|
|
69
69
|
| `position` | `number` | `0` | 0–1 position along the chosen axis |
|
|
70
70
|
| `invertZ` | `boolean` | `false` | Flip the lightness/value axis |
|
|
71
71
|
| `showRaw` | `boolean` | `false` | Bypass nearest-colour matching (shows the raw colour space) |
|
|
72
|
+
| `outlineWidth` | `number` | `0` | Draw a transparent outline where palette regions meet. Width in physical pixels. `0` disables (no overhead). |
|
|
72
73
|
|
|
73
74
|
---
|
|
74
75
|
|
|
@@ -83,6 +84,7 @@ viz.colorModel = 'okhslPolar';
|
|
|
83
84
|
viz.distanceMetric = 'deltaE2000';
|
|
84
85
|
viz.invertZ = true;
|
|
85
86
|
viz.showRaw = true;
|
|
87
|
+
viz.outlineWidth = 2; // transparent border between regions, in physical pixels
|
|
86
88
|
```
|
|
87
89
|
|
|
88
90
|
Additional read-only properties:
|
|
@@ -133,7 +135,7 @@ viz.removeColor('#a8dadc');
|
|
|
133
135
|
|
|
134
136
|
### `destroy()`
|
|
135
137
|
|
|
136
|
-
Cancel the animation frame, release all WebGL resources (
|
|
138
|
+
Cancel the animation frame, release all WebGL resources (programs, textures, framebuffer, buffer, VAO), and remove the canvas from the DOM.
|
|
137
139
|
|
|
138
140
|
---
|
|
139
141
|
|
|
@@ -217,6 +219,28 @@ document.querySelector('#slider').addEventListener('input', (e) => {
|
|
|
217
219
|
});
|
|
218
220
|
```
|
|
219
221
|
|
|
222
|
+
### Transparent outlines between regions
|
|
223
|
+
|
|
224
|
+
`outlineWidth` draws a transparent gap where one palette colour's region meets another, revealing whatever is behind the canvas. Width is in physical pixels (i.e. it already accounts for `pixelRatio`).
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
const viz = new PaletteViz({
|
|
228
|
+
palette,
|
|
229
|
+
outlineWidth: 2,
|
|
230
|
+
container: document.querySelector('#app'),
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// change at runtime — no shader recompile while the value stays > 0
|
|
234
|
+
viz.outlineWidth = 4;
|
|
235
|
+
|
|
236
|
+
// set back to 0 to disable entirely (zero GPU overhead)
|
|
237
|
+
viz.outlineWidth = 0;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Implemented as a two-pass render: pass 1 draws the colour regions into an offscreen framebuffer at the same cost as without outlines; pass 2 runs a tiny edge-detection shader that checks four neighbours via texture reads (no colour-space math). The result is that enabling outlines adds negligible overhead compared to the single-pass approach.
|
|
241
|
+
|
|
242
|
+
When `outlineWidth` is `0` (the default) the framebuffer and outline program are never allocated.
|
|
243
|
+
|
|
220
244
|
### Utility exports
|
|
221
245
|
|
|
222
246
|
```js
|
package/dist/palette-shader.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare const paletteToTexture: (palette: ColorList) => Uint8Array;
|
|
|
14
14
|
|
|
15
15
|
export declare class PaletteViz {
|
|
16
16
|
#private;
|
|
17
|
-
constructor({ palette, width, height, pixelRatio, container, colorModel, distanceMetric, axis, position, invertZ, showRaw, }?: PaletteVizOptions);
|
|
17
|
+
constructor({ palette, width, height, pixelRatio, container, colorModel, distanceMetric, axis, position, invertZ, showRaw, outlineWidth, }?: PaletteVizOptions);
|
|
18
18
|
get canvas(): HTMLCanvasElement;
|
|
19
19
|
get width(): number;
|
|
20
20
|
get height(): number;
|
|
@@ -38,6 +38,8 @@ export declare class PaletteViz {
|
|
|
38
38
|
get invertZ(): boolean;
|
|
39
39
|
set showRaw(value: boolean);
|
|
40
40
|
get showRaw(): boolean;
|
|
41
|
+
set outlineWidth(value: number);
|
|
42
|
+
get outlineWidth(): number;
|
|
41
43
|
static paletteToRGBA: (palette: ColorList) => Uint8Array;
|
|
42
44
|
/** @deprecated use PaletteViz.paletteToRGBA */
|
|
43
45
|
static paletteToTexture: (palette: ColorList) => Uint8Array;
|
|
@@ -55,6 +57,7 @@ declare type PaletteVizOptions = {
|
|
|
55
57
|
position?: number;
|
|
56
58
|
invertZ?: boolean;
|
|
57
59
|
showRaw?: boolean;
|
|
60
|
+
outlineWidth?: number;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
63
|
export declare const randomPalette: (size?: number) => ColorList;
|
package/dist/palette-shader.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const v = `// https://lygia.xyz/
|
|
2
2
|
float srgb2rgb(const in float v) { return (v < 0.04045) ? v * 0.0773993808 : pow((v + 0.055) * 0.947867298578199, 2.4); }
|
|
3
3
|
vec3 srgb2rgb(const in vec3 srgb) { return vec3(srgb2rgb(srgb.r), srgb2rgb(srgb.g), srgb2rgb(srgb.b)); }
|
|
4
4
|
vec4 srgb2rgb(const in vec4 srgb) { return vec4(srgb2rgb(srgb.rgb), srgb.a); }
|
|
5
|
-
`,
|
|
5
|
+
`, L = `// Copyright(c) 2021 Björn Ottosson
|
|
6
6
|
//
|
|
7
7
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
8
8
|
// this softwareand associated documentation files(the "Software"), to deal in
|
|
@@ -654,14 +654,14 @@ vec3 srgb_to_okhsv(vec3 rgb)
|
|
|
654
654
|
float s = (S_0 + T_max) * C_v / ((T_max * S_0) + T_max * k * C_v);
|
|
655
655
|
|
|
656
656
|
return vec3 (h, s, v );
|
|
657
|
-
}`,
|
|
657
|
+
}`, T = `vec3 hsl2rgb( in vec3 c ) {
|
|
658
658
|
vec3 rgb = clamp( abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 );
|
|
659
659
|
return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));
|
|
660
|
-
}`,
|
|
660
|
+
}`, E = `vec3 hsv2rgb(vec3 c) {
|
|
661
661
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
|
662
662
|
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
|
663
663
|
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
|
664
|
-
}`,
|
|
664
|
+
}`, x = `// slightly rearranged vector components so it matches with LCH
|
|
665
665
|
// M_PI and srgb_transfer_function are provided by oklab.frag.glsl (included before this file)
|
|
666
666
|
vec3 lch2rgb(vec3 lch) {
|
|
667
667
|
lch.y *= 0.34;
|
|
@@ -692,7 +692,7 @@ vec3 lch2rgb(vec3 lch) {
|
|
|
692
692
|
srgb_transfer_function(rgb.b)
|
|
693
693
|
);
|
|
694
694
|
}
|
|
695
|
-
`,
|
|
695
|
+
`, R = `// Kotsarenko/Ramos weighted RGB distance.
|
|
696
696
|
// Operates on sRGB values directly (no linearisation needed).
|
|
697
697
|
// Weights red and blue channels by the mean red value, which improves
|
|
698
698
|
// perceptual uniformity compared to plain Euclidean RGB at minimal cost.
|
|
@@ -838,7 +838,7 @@ float deltaE2000(vec3 lab1, vec3 lab2) {
|
|
|
838
838
|
|
|
839
839
|
return sqrt(dLn*dLn + dCn*dCn + dHn*dHn + RT * dCn * dHn);
|
|
840
840
|
}
|
|
841
|
-
`,
|
|
841
|
+
`, S = `// DISTANCE_METRIC define: 0=rgb, 1=oklab, 2=deltaE76, 3=deltaE2000, 4=kotsarenkoRamos, 5=deltaE94
|
|
842
842
|
vec3 closestColor(vec3 color, sampler2D paletteTexture) {
|
|
843
843
|
int paletteSize = textureSize(paletteTexture, 0).x;
|
|
844
844
|
float minDist = 1000000.0;
|
|
@@ -879,14 +879,14 @@ vec3 closestColor(vec3 color, sampler2D paletteTexture) {
|
|
|
879
879
|
|
|
880
880
|
return closest;
|
|
881
881
|
}
|
|
882
|
-
`,
|
|
882
|
+
`, u = `
|
|
883
883
|
precision highp float;
|
|
884
884
|
layout(location = 0) in vec2 a_position;
|
|
885
885
|
out vec2 vUv;
|
|
886
886
|
void main() {
|
|
887
887
|
vUv = a_position * 0.5 + 0.5;
|
|
888
888
|
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
889
|
-
}`,
|
|
889
|
+
}`, k = `
|
|
890
890
|
precision highp float;
|
|
891
891
|
#define TWO_PI 6.28318530718
|
|
892
892
|
in vec2 vUv;
|
|
@@ -894,13 +894,13 @@ out vec4 fragColor;
|
|
|
894
894
|
uniform float progress;
|
|
895
895
|
uniform sampler2D paletteTexture;
|
|
896
896
|
|
|
897
|
-
${g}
|
|
898
|
-
${C}
|
|
899
|
-
${u}
|
|
900
897
|
${v}
|
|
901
898
|
${L}
|
|
902
|
-
${x}
|
|
903
899
|
${T}
|
|
900
|
+
${E}
|
|
901
|
+
${x}
|
|
902
|
+
${R}
|
|
903
|
+
${S}
|
|
904
904
|
|
|
905
905
|
// COLOR_MODEL: 0=rgb, 1=oklab, 2=okhsv, 3=okhsvPolar, 4=okhsl, 5=okhslPolar,
|
|
906
906
|
// 6=oklch, 7=oklchPolar, 8=hsv, 9=hsvPolar, 10=hsl, 11=hslPolar
|
|
@@ -960,14 +960,38 @@ void main(){
|
|
|
960
960
|
#else
|
|
961
961
|
fragColor = vec4(closestColor(rgb, paletteTexture), 1.);
|
|
962
962
|
#endif
|
|
963
|
+
}`, y = `
|
|
964
|
+
precision highp float;
|
|
965
|
+
in vec2 vUv;
|
|
966
|
+
out vec4 fragColor;
|
|
967
|
+
uniform sampler2D colorMap;
|
|
968
|
+
uniform float outlineWidth;
|
|
969
|
+
uniform vec2 resolution;
|
|
970
|
+
|
|
971
|
+
void main() {
|
|
972
|
+
vec4 center = texture(colorMap, vUv);
|
|
973
|
+
if (center.a == 0.0) { fragColor = vec4(0.0); return; }
|
|
974
|
+
vec2 px = outlineWidth / resolution;
|
|
975
|
+
vec4 n0 = texture(colorMap, vUv + vec2( px.x, 0.0));
|
|
976
|
+
vec4 n1 = texture(colorMap, vUv + vec2(-px.x, 0.0));
|
|
977
|
+
vec4 n2 = texture(colorMap, vUv + vec2(0.0, px.y));
|
|
978
|
+
vec4 n3 = texture(colorMap, vUv + vec2(0.0, -px.y));
|
|
979
|
+
if ((n0.a > 0.0 && any(notEqual(n0.rgb, center.rgb))) ||
|
|
980
|
+
(n1.a > 0.0 && any(notEqual(n1.rgb, center.rgb))) ||
|
|
981
|
+
(n2.a > 0.0 && any(notEqual(n2.rgb, center.rgb))) ||
|
|
982
|
+
(n3.a > 0.0 && any(notEqual(n3.rgb, center.rgb)))) {
|
|
983
|
+
fragColor = vec4(0.0);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
fragColor = center;
|
|
963
987
|
}`;
|
|
964
988
|
let _ = null;
|
|
965
|
-
function
|
|
989
|
+
function I(e) {
|
|
966
990
|
if (!_) {
|
|
967
|
-
const
|
|
968
|
-
|
|
991
|
+
const n = document.createElement("canvas");
|
|
992
|
+
n.width = n.height = 1, _ = n.getContext("2d");
|
|
969
993
|
}
|
|
970
|
-
_.fillStyle = "#000000", _.fillStyle =
|
|
994
|
+
_.fillStyle = "#000000", _.fillStyle = e;
|
|
971
995
|
const t = _.fillStyle;
|
|
972
996
|
if (t[0] === "#")
|
|
973
997
|
return [
|
|
@@ -975,72 +999,73 @@ function E(n) {
|
|
|
975
999
|
parseInt(t.slice(3, 5), 16) / 255,
|
|
976
1000
|
parseInt(t.slice(5, 7), 16) / 255
|
|
977
1001
|
];
|
|
978
|
-
const
|
|
979
|
-
return [+
|
|
1002
|
+
const a = t.match(/[\d.]+/g);
|
|
1003
|
+
return [+a[0] / 255, +a[1] / 255, +a[2] / 255];
|
|
980
1004
|
}
|
|
981
|
-
const
|
|
982
|
-
const t = new Uint8Array(
|
|
983
|
-
return
|
|
1005
|
+
const h = (e) => {
|
|
1006
|
+
const t = new Uint8Array(e.length * 4);
|
|
1007
|
+
return e.forEach((a, n) => {
|
|
984
1008
|
try {
|
|
985
|
-
const [r, f, i] =
|
|
986
|
-
t[
|
|
1009
|
+
const [r, f, i] = I(a);
|
|
1010
|
+
t[n * 4 + 0] = Math.round(r * 255), t[n * 4 + 1] = Math.round(f * 255), t[n * 4 + 2] = Math.round(i * 255), t[n * 4 + 3] = 255;
|
|
987
1011
|
} catch {
|
|
988
|
-
console.error(`Invalid color: ${
|
|
1012
|
+
console.error(`Invalid color: ${a}`);
|
|
989
1013
|
}
|
|
990
1014
|
}), t;
|
|
991
|
-
},
|
|
992
|
-
{ length:
|
|
1015
|
+
}, A = h, O = (e = 20) => Array.from(
|
|
1016
|
+
{ length: e },
|
|
993
1017
|
() => `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`
|
|
994
1018
|
);
|
|
995
|
-
function p(
|
|
996
|
-
const
|
|
997
|
-
if (
|
|
998
|
-
const r =
|
|
999
|
-
throw
|
|
1019
|
+
function p(e, t, a) {
|
|
1020
|
+
const n = e.createShader(t);
|
|
1021
|
+
if (e.shaderSource(n, a), e.compileShader(n), !e.getShaderParameter(n, e.COMPILE_STATUS)) {
|
|
1022
|
+
const r = e.getShaderInfoLog(n);
|
|
1023
|
+
throw e.deleteShader(n), new Error(`Shader compile error:
|
|
1000
1024
|
${r}`);
|
|
1001
1025
|
}
|
|
1002
|
-
return
|
|
1026
|
+
return n;
|
|
1003
1027
|
}
|
|
1004
|
-
function
|
|
1028
|
+
function m(e, t, a, n) {
|
|
1005
1029
|
const f = `#version 300 es
|
|
1006
|
-
` + (Object.entries(t).filter(([, s]) => s !== !1).map(([s,
|
|
1030
|
+
` + (Object.entries(t).filter(([, s]) => s !== !1).map(([s, d]) => `#define ${s} ${d}`).join(`
|
|
1007
1031
|
`) + `
|
|
1008
|
-
`), i = p(
|
|
1009
|
-
if (
|
|
1010
|
-
const s =
|
|
1011
|
-
throw
|
|
1032
|
+
`), i = p(e, e.VERTEX_SHADER, f + n), b = p(e, e.FRAGMENT_SHADER, f + a), l = e.createProgram();
|
|
1033
|
+
if (e.attachShader(l, i), e.attachShader(l, b), e.linkProgram(l), e.deleteShader(i), e.deleteShader(b), !e.getProgramParameter(l, e.LINK_STATUS)) {
|
|
1034
|
+
const s = e.getProgramInfoLog(l);
|
|
1035
|
+
throw e.deleteProgram(l), new Error(`Program link error:
|
|
1012
1036
|
${s}`);
|
|
1013
1037
|
}
|
|
1014
1038
|
return l;
|
|
1015
1039
|
}
|
|
1016
|
-
function c(
|
|
1017
|
-
|
|
1018
|
-
|
|
1040
|
+
function c(e, t, a) {
|
|
1041
|
+
e.bindTexture(e.TEXTURE_2D, t), e.texImage2D(
|
|
1042
|
+
e.TEXTURE_2D,
|
|
1019
1043
|
0,
|
|
1020
|
-
|
|
1021
|
-
|
|
1044
|
+
e.RGBA8,
|
|
1045
|
+
a.length,
|
|
1022
1046
|
1,
|
|
1023
1047
|
0,
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
),
|
|
1048
|
+
e.RGBA,
|
|
1049
|
+
e.UNSIGNED_BYTE,
|
|
1050
|
+
h(a)
|
|
1051
|
+
), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE);
|
|
1028
1052
|
}
|
|
1029
|
-
class
|
|
1053
|
+
class w {
|
|
1030
1054
|
#t = [];
|
|
1031
|
-
#
|
|
1032
|
-
#
|
|
1033
|
-
#
|
|
1055
|
+
#b = 512;
|
|
1056
|
+
#h = 512;
|
|
1057
|
+
#L = 1;
|
|
1034
1058
|
// shader state
|
|
1035
|
-
#
|
|
1036
|
-
#
|
|
1037
|
-
#
|
|
1038
|
-
#
|
|
1039
|
-
#
|
|
1040
|
-
#
|
|
1059
|
+
#d = 0;
|
|
1060
|
+
#u = "y";
|
|
1061
|
+
#p = "okhsv";
|
|
1062
|
+
#m = "oklab";
|
|
1063
|
+
#g = !1;
|
|
1064
|
+
#C = !1;
|
|
1065
|
+
#i = 0;
|
|
1041
1066
|
// uniform value maps
|
|
1042
|
-
#
|
|
1043
|
-
#
|
|
1067
|
+
#E = { x: 0, y: 1, z: 2 };
|
|
1068
|
+
#x = {
|
|
1044
1069
|
rgb: 0,
|
|
1045
1070
|
oklab: 1,
|
|
1046
1071
|
okhsv: 2,
|
|
@@ -1054,7 +1079,7 @@ class O {
|
|
|
1054
1079
|
hsl: 10,
|
|
1055
1080
|
hslPolar: 11
|
|
1056
1081
|
};
|
|
1057
|
-
#
|
|
1082
|
+
#R = {
|
|
1058
1083
|
rgb: 0,
|
|
1059
1084
|
oklab: 1,
|
|
1060
1085
|
deltaE76: 2,
|
|
@@ -1063,149 +1088,180 @@ class O {
|
|
|
1063
1088
|
deltaE94: 5
|
|
1064
1089
|
};
|
|
1065
1090
|
// WebGL
|
|
1066
|
-
#
|
|
1091
|
+
#n;
|
|
1067
1092
|
#a;
|
|
1068
|
-
#
|
|
1093
|
+
#f = null;
|
|
1069
1094
|
#o = null;
|
|
1070
|
-
#C = null;
|
|
1071
|
-
#m = null;
|
|
1072
|
-
#l = null;
|
|
1073
|
-
// cached uniform locations (re-queried after each program rebuild)
|
|
1074
|
-
#x = null;
|
|
1075
1095
|
#T = null;
|
|
1096
|
+
#v = null;
|
|
1097
|
+
#_ = null;
|
|
1098
|
+
// cached uniform locations (re-queried after each program rebuild)
|
|
1099
|
+
#S = null;
|
|
1100
|
+
#k = null;
|
|
1101
|
+
// outline pass (created/destroyed when outlineWidth toggles between 0 and >0)
|
|
1102
|
+
#r = null;
|
|
1103
|
+
#l = null;
|
|
1104
|
+
#s = null;
|
|
1105
|
+
#y = null;
|
|
1106
|
+
#I = null;
|
|
1107
|
+
#O = null;
|
|
1076
1108
|
// dom
|
|
1077
|
-
#
|
|
1109
|
+
#A;
|
|
1078
1110
|
constructor({
|
|
1079
|
-
palette: t =
|
|
1080
|
-
width:
|
|
1081
|
-
height:
|
|
1111
|
+
palette: t = O(),
|
|
1112
|
+
width: a = 512,
|
|
1113
|
+
height: n = 512,
|
|
1082
1114
|
pixelRatio: r = window.devicePixelRatio,
|
|
1083
1115
|
container: f,
|
|
1084
1116
|
colorModel: i = "okhsv",
|
|
1085
1117
|
distanceMetric: b = "oklab",
|
|
1086
1118
|
axis: l = "y",
|
|
1087
1119
|
position: s = 0,
|
|
1088
|
-
invertZ:
|
|
1089
|
-
showRaw:
|
|
1120
|
+
invertZ: d = !1,
|
|
1121
|
+
showRaw: g = !1,
|
|
1122
|
+
outlineWidth: C = 0
|
|
1090
1123
|
} = {}) {
|
|
1091
|
-
this.#t = t, this.#
|
|
1092
|
-
const o = this.#
|
|
1124
|
+
this.#t = t, this.#b = a, this.#h = n, this.#L = r, this.#p = i, this.#m = b, this.#u = l, this.#d = s, this.#g = d, this.#C = g, this.#i = C, this.#A = f, this.#n = document.createElement("canvas"), this.#n.classList.add("palette-viz");
|
|
1125
|
+
const o = this.#n.getContext("webgl2");
|
|
1093
1126
|
if (!o) throw new Error("WebGL2 not supported");
|
|
1094
|
-
this.#a = o, this.#
|
|
1127
|
+
this.#a = o, this.#T = o.createBuffer(), o.bindBuffer(o.ARRAY_BUFFER, this.#T), o.bufferData(o.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), o.STATIC_DRAW), this.#v = o.createVertexArray(), o.bindVertexArray(this.#v), o.enableVertexAttribArray(0), o.vertexAttribPointer(0, 2, o.FLOAT, !1, 0, 0), o.bindVertexArray(null), this.#o = o.createTexture(), c(o, this.#o, this.#t), this.#c(), this.#D(this.#b, this.#h), this.#i > 0 && this.#w(), this.#A?.appendChild(this.#n), this.#e();
|
|
1095
1128
|
}
|
|
1096
|
-
#
|
|
1129
|
+
#z() {
|
|
1097
1130
|
return {
|
|
1098
|
-
DISTANCE_METRIC: this.#
|
|
1099
|
-
COLOR_MODEL: this.#
|
|
1100
|
-
PROGRESS_AXIS: this.#
|
|
1101
|
-
INVERT_Z: this.#
|
|
1102
|
-
SHOW_RAW: this.#
|
|
1131
|
+
DISTANCE_METRIC: this.#R[this.#m],
|
|
1132
|
+
COLOR_MODEL: this.#x[this.#p],
|
|
1133
|
+
PROGRESS_AXIS: this.#E[this.#u],
|
|
1134
|
+
INVERT_Z: this.#g ? 1 : !1,
|
|
1135
|
+
SHOW_RAW: this.#C ? 1 : !1
|
|
1103
1136
|
};
|
|
1104
1137
|
}
|
|
1105
|
-
#
|
|
1138
|
+
#c() {
|
|
1106
1139
|
const t = this.#a;
|
|
1107
|
-
this.#
|
|
1140
|
+
this.#f && t.deleteProgram(this.#f), this.#f = m(t, this.#z(), k, u), this.#S = t.getUniformLocation(this.#f, "progress"), this.#k = t.getUniformLocation(this.#f, "paletteTexture");
|
|
1108
1141
|
}
|
|
1109
|
-
#
|
|
1110
|
-
const
|
|
1111
|
-
this.#
|
|
1142
|
+
#w() {
|
|
1143
|
+
const t = this.#a;
|
|
1144
|
+
this.#s = m(t, {}, y, u), this.#y = t.getUniformLocation(this.#s, "colorMap"), this.#I = t.getUniformLocation(this.#s, "outlineWidth"), this.#O = t.getUniformLocation(this.#s, "resolution"), this.#l = t.createTexture(), this.#r = t.createFramebuffer(), this.#P(this.#n.width, this.#n.height);
|
|
1112
1145
|
}
|
|
1113
|
-
#
|
|
1114
|
-
|
|
1146
|
+
#M() {
|
|
1147
|
+
const t = this.#a;
|
|
1148
|
+
this.#s && (t.deleteProgram(this.#s), this.#s = null), this.#l && (t.deleteTexture(this.#l), this.#l = null), this.#r && (t.deleteFramebuffer(this.#r), this.#r = null);
|
|
1149
|
+
}
|
|
1150
|
+
#P(t, a) {
|
|
1151
|
+
const n = this.#a;
|
|
1152
|
+
n.bindTexture(n.TEXTURE_2D, this.#l), n.texImage2D(n.TEXTURE_2D, 0, n.RGBA8, t, a, 0, n.RGBA, n.UNSIGNED_BYTE, null), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_MIN_FILTER, n.NEAREST), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_MAG_FILTER, n.NEAREST), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_WRAP_S, n.CLAMP_TO_EDGE), n.texParameteri(n.TEXTURE_2D, n.TEXTURE_WRAP_T, n.CLAMP_TO_EDGE), n.bindTexture(n.TEXTURE_2D, null), n.bindFramebuffer(n.FRAMEBUFFER, this.#r), n.framebufferTexture2D(n.FRAMEBUFFER, n.COLOR_ATTACHMENT0, n.TEXTURE_2D, this.#l, 0), n.bindFramebuffer(n.FRAMEBUFFER, null);
|
|
1153
|
+
}
|
|
1154
|
+
#D(t, a) {
|
|
1155
|
+
const n = Math.round(t * this.#L), r = Math.round(a * this.#L);
|
|
1156
|
+
this.#n.width = n, this.#n.height = r, this.#n.style.width = `${t}px`, this.#n.style.height = `${a}px`, this.#a.viewport(0, 0, n, r), this.#l && this.#P(n, r);
|
|
1157
|
+
}
|
|
1158
|
+
#e() {
|
|
1159
|
+
this.#_ !== null && cancelAnimationFrame(this.#_), this.#_ = requestAnimationFrame(() => {
|
|
1115
1160
|
const t = this.#a;
|
|
1116
|
-
t.
|
|
1161
|
+
if (this.#r && t.bindFramebuffer(t.FRAMEBUFFER, this.#r), t.useProgram(this.#f), t.uniform1f(this.#S, this.#d), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.#o), t.uniform1i(this.#k, 0), t.clearColor(0, 0, 0, 0), t.clear(t.COLOR_BUFFER_BIT), t.bindVertexArray(this.#v), t.drawArrays(t.TRIANGLE_STRIP, 0, 4), !this.#r) {
|
|
1162
|
+
t.bindVertexArray(null);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
t.bindFramebuffer(t.FRAMEBUFFER, null), t.useProgram(this.#s), t.activeTexture(t.TEXTURE0), t.bindTexture(t.TEXTURE_2D, this.#l), t.uniform1i(this.#y, 0), t.uniform1f(this.#I, this.#i), t.uniform2f(this.#O, this.#n.width, this.#n.height), t.clearColor(0, 0, 0, 0), t.clear(t.COLOR_BUFFER_BIT), t.drawArrays(t.TRIANGLE_STRIP, 0, 4), t.bindVertexArray(null);
|
|
1117
1166
|
});
|
|
1118
1167
|
}
|
|
1119
1168
|
// ── Public API ──────────────────────────────────────────────────────────────
|
|
1120
1169
|
get canvas() {
|
|
1121
|
-
return this.#
|
|
1170
|
+
return this.#n;
|
|
1122
1171
|
}
|
|
1123
1172
|
get width() {
|
|
1124
|
-
return this.#
|
|
1173
|
+
return this.#b;
|
|
1125
1174
|
}
|
|
1126
1175
|
get height() {
|
|
1127
|
-
return this.#
|
|
1176
|
+
return this.#h;
|
|
1128
1177
|
}
|
|
1129
|
-
resize(t,
|
|
1130
|
-
this.#
|
|
1178
|
+
resize(t, a = null) {
|
|
1179
|
+
this.#b = t, this.#h = a ?? t, this.#D(this.#b, this.#h), this.#e();
|
|
1131
1180
|
}
|
|
1132
1181
|
destroy() {
|
|
1133
|
-
this.#
|
|
1182
|
+
this.#_ !== null && (cancelAnimationFrame(this.#_), this.#_ = null);
|
|
1134
1183
|
const t = this.#a;
|
|
1135
|
-
t.deleteProgram(this.#
|
|
1184
|
+
this.#M(), t.deleteProgram(this.#f), t.deleteTexture(this.#o), t.deleteBuffer(this.#T), t.deleteVertexArray(this.#v), this.#n.remove(), t.getExtension("WEBGL_lose_context")?.loseContext();
|
|
1136
1185
|
}
|
|
1137
1186
|
// ── Palette ─────────────────────────────────────────────────────────────────
|
|
1138
1187
|
set palette(t) {
|
|
1139
|
-
this.#t = t, c(this.#a, this.#o, t), this.#
|
|
1188
|
+
this.#t = t, c(this.#a, this.#o, t), this.#e();
|
|
1140
1189
|
}
|
|
1141
1190
|
get palette() {
|
|
1142
1191
|
return this.#t;
|
|
1143
1192
|
}
|
|
1144
|
-
setColor(t,
|
|
1145
|
-
if (
|
|
1146
|
-
this.#t[
|
|
1193
|
+
setColor(t, a) {
|
|
1194
|
+
if (a < 0 || a >= this.#t.length) throw new Error(`Index ${a} out of range`);
|
|
1195
|
+
this.#t[a] = t, c(this.#a, this.#o, this.#t), this.#e();
|
|
1147
1196
|
}
|
|
1148
|
-
addColor(t,
|
|
1149
|
-
this.#t.splice(
|
|
1197
|
+
addColor(t, a) {
|
|
1198
|
+
this.#t.splice(a ?? this.#t.length, 0, t), c(this.#a, this.#o, this.#t), this.#e();
|
|
1150
1199
|
}
|
|
1151
1200
|
removeColor(t) {
|
|
1152
|
-
const
|
|
1153
|
-
if (
|
|
1154
|
-
if (
|
|
1155
|
-
this.#t.splice(
|
|
1201
|
+
const a = typeof t == "number" ? t : this.#t.indexOf(t);
|
|
1202
|
+
if (a === -1) throw new Error("Color not found in palette");
|
|
1203
|
+
if (a < 0 || a >= this.#t.length) throw new Error(`Index ${a} out of range`);
|
|
1204
|
+
this.#t.splice(a, 1), c(this.#a, this.#o, this.#t), this.#e();
|
|
1156
1205
|
}
|
|
1157
1206
|
// ── Shader properties ────────────────────────────────────────────────────────
|
|
1158
1207
|
set position(t) {
|
|
1159
|
-
this.#
|
|
1208
|
+
this.#d = t, this.#e();
|
|
1160
1209
|
}
|
|
1161
1210
|
get position() {
|
|
1162
|
-
return this.#
|
|
1211
|
+
return this.#d;
|
|
1163
1212
|
}
|
|
1164
1213
|
set axis(t) {
|
|
1165
|
-
if (!(t in this.#
|
|
1166
|
-
this.#
|
|
1214
|
+
if (!(t in this.#E)) throw new Error("axis must be 'x', 'y', or 'z'");
|
|
1215
|
+
this.#u = t, this.#c(), this.#e();
|
|
1167
1216
|
}
|
|
1168
1217
|
get axis() {
|
|
1169
|
-
return this.#
|
|
1218
|
+
return this.#u;
|
|
1170
1219
|
}
|
|
1171
1220
|
set colorModel(t) {
|
|
1172
|
-
if (!(t in this.#
|
|
1173
|
-
this.#
|
|
1221
|
+
if (!(t in this.#x)) throw new Error(`colorModel '${t}' is not supported`);
|
|
1222
|
+
this.#p = t, this.#c(), this.#e();
|
|
1174
1223
|
}
|
|
1175
1224
|
get colorModel() {
|
|
1176
|
-
return this.#
|
|
1225
|
+
return this.#p;
|
|
1177
1226
|
}
|
|
1178
1227
|
set distanceMetric(t) {
|
|
1179
|
-
if (!(t in this.#
|
|
1228
|
+
if (!(t in this.#R))
|
|
1180
1229
|
throw new Error(
|
|
1181
1230
|
"distanceMetric must be 'rgb', 'oklab', 'deltaE76', 'deltaE94', 'deltaE2000', or 'kotsarenkoRamos'"
|
|
1182
1231
|
);
|
|
1183
|
-
this.#
|
|
1232
|
+
this.#m = t, this.#c(), this.#e();
|
|
1184
1233
|
}
|
|
1185
1234
|
get distanceMetric() {
|
|
1186
|
-
return this.#
|
|
1235
|
+
return this.#m;
|
|
1187
1236
|
}
|
|
1188
1237
|
set invertZ(t) {
|
|
1189
|
-
this.#
|
|
1238
|
+
this.#g = t, this.#c(), this.#e();
|
|
1190
1239
|
}
|
|
1191
1240
|
get invertZ() {
|
|
1192
|
-
return this.#
|
|
1241
|
+
return this.#g;
|
|
1193
1242
|
}
|
|
1194
1243
|
set showRaw(t) {
|
|
1195
|
-
this.#
|
|
1244
|
+
this.#C = t, this.#c(), this.#e();
|
|
1196
1245
|
}
|
|
1197
1246
|
get showRaw() {
|
|
1198
|
-
return this.#
|
|
1247
|
+
return this.#C;
|
|
1248
|
+
}
|
|
1249
|
+
set outlineWidth(t) {
|
|
1250
|
+
const a = this.#i > 0;
|
|
1251
|
+
this.#i = t, t > 0 !== a && (t > 0 ? this.#w() : this.#M()), this.#e();
|
|
1252
|
+
}
|
|
1253
|
+
get outlineWidth() {
|
|
1254
|
+
return this.#i;
|
|
1199
1255
|
}
|
|
1200
|
-
static paletteToRGBA =
|
|
1256
|
+
static paletteToRGBA = h;
|
|
1201
1257
|
/** @deprecated use PaletteViz.paletteToRGBA */
|
|
1202
|
-
static paletteToTexture =
|
|
1258
|
+
static paletteToTexture = h;
|
|
1203
1259
|
}
|
|
1204
1260
|
export {
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1261
|
+
w as PaletteViz,
|
|
1262
|
+
k as fragmentShader,
|
|
1263
|
+
h as paletteToRGBA,
|
|
1264
|
+
A as paletteToTexture,
|
|
1265
|
+
O as randomPalette
|
|
1210
1266
|
};
|
|
1211
1267
|
//# sourceMappingURL=palette-shader.js.map
|