1 | import { clamp, getRandom, getRangeMax, getRangeMin, getRangeValue, mix, randomInRange, setRangeValue, } from "./NumberUtils.js";
|
2 | import { isArray, isString } from "./TypeUtils.js";
|
3 | import { millisecondsToSeconds, percentDenominator } from "../Core/Utils/Constants.js";
|
4 | import { AnimationStatus } from "../Enums/AnimationStatus.js";
|
5 | import { itemFromArray } from "./Utils.js";
|
6 | var RgbIndexes;
|
7 | (function (RgbIndexes) {
|
8 | RgbIndexes[RgbIndexes["r"] = 1] = "r";
|
9 | RgbIndexes[RgbIndexes["g"] = 2] = "g";
|
10 | RgbIndexes[RgbIndexes["b"] = 3] = "b";
|
11 | RgbIndexes[RgbIndexes["a"] = 4] = "a";
|
12 | })(RgbIndexes || (RgbIndexes = {}));
|
13 | const randomColorValue = "random", midColorValue = "mid", colorManagers = new Map();
|
14 | export function addColorManager(manager) {
|
15 | colorManagers.set(manager.key, manager);
|
16 | }
|
17 | function stringToRgba(input) {
|
18 | for (const [, manager] of colorManagers) {
|
19 | if (input.startsWith(manager.stringPrefix)) {
|
20 | return manager.parseString(input);
|
21 | }
|
22 | }
|
23 | const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i, hexFixed = input.replace(shorthandRegex, (_, r, g, b, a) => {
|
24 | return r + r + g + g + b + b + (a !== undefined ? a + a : "");
|
25 | }), regex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i, result = regex.exec(hexFixed), radix = 16, defaultAlpha = 1, alphaFactor = 0xff;
|
26 | return result
|
27 | ? {
|
28 | a: result[RgbIndexes.a] !== undefined
|
29 | ? parseInt(result[RgbIndexes.a], radix) / alphaFactor
|
30 | : defaultAlpha,
|
31 | b: parseInt(result[RgbIndexes.b], radix),
|
32 | g: parseInt(result[RgbIndexes.g], radix),
|
33 | r: parseInt(result[RgbIndexes.r], radix),
|
34 | }
|
35 | : undefined;
|
36 | }
|
37 | export function rangeColorToRgb(input, index, useIndex = true) {
|
38 | if (!input) {
|
39 | return;
|
40 | }
|
41 | const color = isString(input) ? { value: input } : input;
|
42 | if (isString(color.value)) {
|
43 | return colorToRgb(color.value, index, useIndex);
|
44 | }
|
45 | if (isArray(color.value)) {
|
46 | return rangeColorToRgb({
|
47 | value: itemFromArray(color.value, index, useIndex),
|
48 | });
|
49 | }
|
50 | for (const [, manager] of colorManagers) {
|
51 | const res = manager.handleRangeColor(color);
|
52 | if (res) {
|
53 | return res;
|
54 | }
|
55 | }
|
56 | }
|
57 | export function colorToRgb(input, index, useIndex = true) {
|
58 | if (!input) {
|
59 | return;
|
60 | }
|
61 | const color = isString(input) ? { value: input } : input;
|
62 | if (isString(color.value)) {
|
63 | return color.value === randomColorValue ? getRandomRgbColor() : stringToRgb(color.value);
|
64 | }
|
65 | if (isArray(color.value)) {
|
66 | return colorToRgb({
|
67 | value: itemFromArray(color.value, index, useIndex),
|
68 | });
|
69 | }
|
70 | for (const [, manager] of colorManagers) {
|
71 | const res = manager.handleColor(color);
|
72 | if (res) {
|
73 | return res;
|
74 | }
|
75 | }
|
76 | }
|
77 | export function colorToHsl(color, index, useIndex = true) {
|
78 | const rgb = colorToRgb(color, index, useIndex);
|
79 | return rgb ? rgbToHsl(rgb) : undefined;
|
80 | }
|
81 | export function rangeColorToHsl(color, index, useIndex = true) {
|
82 | const rgb = rangeColorToRgb(color, index, useIndex);
|
83 | return rgb ? rgbToHsl(rgb) : undefined;
|
84 | }
|
85 | export function rgbToHsl(color) {
|
86 | const rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, half = 0.5, double = 2, r1 = color.r / rgbMax, g1 = color.g / rgbMax, b1 = color.b / rgbMax, max = Math.max(r1, g1, b1), min = Math.min(r1, g1, b1), res = {
|
87 | h: hMin,
|
88 | l: (max + min) * half,
|
89 | s: sMin,
|
90 | };
|
91 | if (max !== min) {
|
92 | res.s = res.l < half ? (max - min) / (max + min) : (max - min) / (double - max - min);
|
93 | res.h =
|
94 | r1 === max
|
95 | ? (g1 - b1) / (max - min)
|
96 | : (res.h = g1 === max ? double + (b1 - r1) / (max - min) : double * double + (r1 - g1) / (max - min));
|
97 | }
|
98 | res.l *= lMax;
|
99 | res.s *= sMax;
|
100 | res.h *= hPhase;
|
101 | if (res.h < hMin) {
|
102 | res.h += hMax;
|
103 | }
|
104 | if (res.h >= hMax) {
|
105 | res.h -= hMax;
|
106 | }
|
107 | return res;
|
108 | }
|
109 | export function stringToAlpha(input) {
|
110 | return stringToRgba(input)?.a;
|
111 | }
|
112 | export function stringToRgb(input) {
|
113 | return stringToRgba(input);
|
114 | }
|
115 | export function hslToRgb(hsl) {
|
116 | const hMax = 360, sMax = 100, lMax = 100, sMin = 0, lMin = 0, h = ((hsl.h % hMax) + hMax) % hMax, s = Math.max(sMin, Math.min(sMax, hsl.s)), l = Math.max(lMin, Math.min(lMax, hsl.l)), hNormalized = h / hMax, sNormalized = s / sMax, lNormalized = l / lMax, rgbFactor = 255, triple = 3;
|
117 | if (s === sMin) {
|
118 | const grayscaleValue = Math.round(lNormalized * rgbFactor);
|
119 | return { r: grayscaleValue, g: grayscaleValue, b: grayscaleValue };
|
120 | }
|
121 | const half = 0.5, double = 2, channel = (temp1, temp2, temp3) => {
|
122 | const temp3Min = 0, temp3Max = 1, sextuple = 6;
|
123 | if (temp3 < temp3Min) {
|
124 | temp3++;
|
125 | }
|
126 | if (temp3 > temp3Max) {
|
127 | temp3--;
|
128 | }
|
129 | if (temp3 * sextuple < temp3Max) {
|
130 | return temp1 + (temp2 - temp1) * sextuple * temp3;
|
131 | }
|
132 | if (temp3 * double < temp3Max) {
|
133 | return temp2;
|
134 | }
|
135 | if (temp3 * triple < temp3Max * double) {
|
136 | const temp3Offset = double / triple;
|
137 | return temp1 + (temp2 - temp1) * (temp3Offset - temp3) * sextuple;
|
138 | }
|
139 | return temp1;
|
140 | }, sNormalizedOffset = 1, temp1 = lNormalized < half
|
141 | ? lNormalized * (sNormalizedOffset + sNormalized)
|
142 | : lNormalized + sNormalized - lNormalized * sNormalized, temp2 = double * lNormalized - temp1, phaseNumerator = 1, phaseThird = phaseNumerator / triple, red = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized + phaseThird)), green = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized)), blue = Math.min(rgbFactor, rgbFactor * channel(temp2, temp1, hNormalized - phaseThird));
|
143 | return { r: Math.round(red), g: Math.round(green), b: Math.round(blue) };
|
144 | }
|
145 | export function hslaToRgba(hsla) {
|
146 | const rgbResult = hslToRgb(hsla);
|
147 | return {
|
148 | a: hsla.a,
|
149 | b: rgbResult.b,
|
150 | g: rgbResult.g,
|
151 | r: rgbResult.r,
|
152 | };
|
153 | }
|
154 | export function getRandomRgbColor(min) {
|
155 | const defaultMin = 0, fixedMin = min ?? defaultMin, rgbMax = 256;
|
156 | return {
|
157 | b: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
158 | g: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
159 | r: Math.floor(randomInRange(setRangeValue(fixedMin, rgbMax))),
|
160 | };
|
161 | }
|
162 | export function getStyleFromRgb(color, opacity) {
|
163 | const defaultOpacity = 1;
|
164 | return `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity ?? defaultOpacity})`;
|
165 | }
|
166 | export function getStyleFromHsl(color, opacity) {
|
167 | const defaultOpacity = 1;
|
168 | return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${opacity ?? defaultOpacity})`;
|
169 | }
|
170 | export function colorMix(color1, color2, size1, size2) {
|
171 | let rgb1 = color1, rgb2 = color2;
|
172 | if (rgb1.r === undefined) {
|
173 | rgb1 = hslToRgb(color1);
|
174 | }
|
175 | if (rgb2.r === undefined) {
|
176 | rgb2 = hslToRgb(color2);
|
177 | }
|
178 | return {
|
179 | b: mix(rgb1.b, rgb2.b, size1, size2),
|
180 | g: mix(rgb1.g, rgb2.g, size1, size2),
|
181 | r: mix(rgb1.r, rgb2.r, size1, size2),
|
182 | };
|
183 | }
|
184 | export function getLinkColor(p1, p2, linkColor) {
|
185 | if (linkColor === randomColorValue) {
|
186 | return getRandomRgbColor();
|
187 | }
|
188 | else if (linkColor === midColorValue) {
|
189 | const sourceColor = p1.getFillColor() ?? p1.getStrokeColor(), destColor = p2?.getFillColor() ?? p2?.getStrokeColor();
|
190 | if (sourceColor && destColor && p2) {
|
191 | return colorMix(sourceColor, destColor, p1.getRadius(), p2.getRadius());
|
192 | }
|
193 | else {
|
194 | const hslColor = sourceColor ?? destColor;
|
195 | if (hslColor) {
|
196 | return hslToRgb(hslColor);
|
197 | }
|
198 | }
|
199 | }
|
200 | else {
|
201 | return linkColor;
|
202 | }
|
203 | }
|
204 | export function getLinkRandomColor(optColor, blink, consent) {
|
205 | const color = isString(optColor) ? optColor : optColor.value;
|
206 | if (color === randomColorValue) {
|
207 | if (consent) {
|
208 | return rangeColorToRgb({
|
209 | value: color,
|
210 | });
|
211 | }
|
212 | if (blink) {
|
213 | return randomColorValue;
|
214 | }
|
215 | return midColorValue;
|
216 | }
|
217 | else if (color === midColorValue) {
|
218 | return midColorValue;
|
219 | }
|
220 | else {
|
221 | return rangeColorToRgb({
|
222 | value: color,
|
223 | });
|
224 | }
|
225 | }
|
226 | export function getHslFromAnimation(animation) {
|
227 | return animation !== undefined
|
228 | ? {
|
229 | h: animation.h.value,
|
230 | s: animation.s.value,
|
231 | l: animation.l.value,
|
232 | }
|
233 | : undefined;
|
234 | }
|
235 | export function getHslAnimationFromHsl(hsl, animationOptions, reduceFactor) {
|
236 | const resColor = {
|
237 | h: {
|
238 | enable: false,
|
239 | value: hsl.h,
|
240 | },
|
241 | s: {
|
242 | enable: false,
|
243 | value: hsl.s,
|
244 | },
|
245 | l: {
|
246 | enable: false,
|
247 | value: hsl.l,
|
248 | },
|
249 | };
|
250 | if (animationOptions) {
|
251 | setColorAnimation(resColor.h, animationOptions.h, reduceFactor);
|
252 | setColorAnimation(resColor.s, animationOptions.s, reduceFactor);
|
253 | setColorAnimation(resColor.l, animationOptions.l, reduceFactor);
|
254 | }
|
255 | return resColor;
|
256 | }
|
257 | function setColorAnimation(colorValue, colorAnimation, reduceFactor) {
|
258 | colorValue.enable = colorAnimation.enable;
|
259 | const defaultVelocity = 0, decayOffset = 1, defaultLoops = 0, defaultTime = 0;
|
260 | if (colorValue.enable) {
|
261 | colorValue.velocity = (getRangeValue(colorAnimation.speed) / percentDenominator) * reduceFactor;
|
262 | colorValue.decay = decayOffset - getRangeValue(colorAnimation.decay);
|
263 | colorValue.status = AnimationStatus.increasing;
|
264 | colorValue.loops = defaultLoops;
|
265 | colorValue.maxLoops = getRangeValue(colorAnimation.count);
|
266 | colorValue.time = defaultTime;
|
267 | colorValue.delayTime = getRangeValue(colorAnimation.delay) * millisecondsToSeconds;
|
268 | if (!colorAnimation.sync) {
|
269 | colorValue.velocity *= getRandom();
|
270 | colorValue.value *= getRandom();
|
271 | }
|
272 | colorValue.initialValue = colorValue.value;
|
273 | colorValue.offset = setRangeValue(colorAnimation.offset);
|
274 | }
|
275 | else {
|
276 | colorValue.velocity = defaultVelocity;
|
277 | }
|
278 | }
|
279 | export function updateColorValue(data, range, decrease, delta) {
|
280 | const minLoops = 0, minDelay = 0, identity = 1, minVelocity = 0, minOffset = 0, velocityFactor = 3.6;
|
281 | if (!data ||
|
282 | !data.enable ||
|
283 | ((data.maxLoops ?? minLoops) > minLoops && (data.loops ?? minLoops) > (data.maxLoops ?? minLoops))) {
|
284 | return;
|
285 | }
|
286 | if (!data.time) {
|
287 | data.time = 0;
|
288 | }
|
289 | if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
290 | data.time += delta.value;
|
291 | }
|
292 | if ((data.delayTime ?? minDelay) > minDelay && data.time < (data.delayTime ?? minDelay)) {
|
293 | return;
|
294 | }
|
295 | const offset = data.offset ? randomInRange(data.offset) : minOffset, velocity = (data.velocity ?? minVelocity) * delta.factor + offset * velocityFactor, decay = data.decay ?? identity, max = getRangeMax(range), min = getRangeMin(range);
|
296 | if (!decrease || data.status === AnimationStatus.increasing) {
|
297 | data.value += velocity;
|
298 | if (data.value > max) {
|
299 | if (!data.loops) {
|
300 | data.loops = 0;
|
301 | }
|
302 | data.loops++;
|
303 | if (decrease) {
|
304 | data.status = AnimationStatus.decreasing;
|
305 | }
|
306 | else {
|
307 | data.value -= max;
|
308 | }
|
309 | }
|
310 | }
|
311 | else {
|
312 | data.value -= velocity;
|
313 | const minValue = 0;
|
314 | if (data.value < minValue) {
|
315 | if (!data.loops) {
|
316 | data.loops = 0;
|
317 | }
|
318 | data.loops++;
|
319 | data.status = AnimationStatus.increasing;
|
320 | }
|
321 | }
|
322 | if (data.velocity && decay !== identity) {
|
323 | data.velocity *= decay;
|
324 | }
|
325 | data.value = clamp(data.value, min, max);
|
326 | }
|
327 | export function updateColor(color, delta) {
|
328 | if (!color) {
|
329 | return;
|
330 | }
|
331 | const { h, s, l } = color;
|
332 | const ranges = {
|
333 | h: { min: 0, max: 360 },
|
334 | s: { min: 0, max: 100 },
|
335 | l: { min: 0, max: 100 },
|
336 | };
|
337 | if (h) {
|
338 | updateColorValue(h, ranges.h, false, delta);
|
339 | }
|
340 | if (s) {
|
341 | updateColorValue(s, ranges.s, true, delta);
|
342 | }
|
343 | if (l) {
|
344 | updateColorValue(l, ranges.l, true, delta);
|
345 | }
|
346 | }
|