UNPKG

12.5 kBJavaScriptView Raw
1import { clamp, getRandom, getRangeMax, getRangeMin, getRangeValue, mix, randomInRange, setRangeValue, } from "./NumberUtils.js";
2import { isArray, isString } from "./TypeUtils.js";
3import { millisecondsToSeconds, percentDenominator } from "../Core/Utils/Constants.js";
4import { AnimationStatus } from "../Enums/AnimationStatus.js";
5import { itemFromArray } from "./Utils.js";
6var 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 = {}));
13const randomColorValue = "random", midColorValue = "mid", colorManagers = new Map();
14export function addColorManager(manager) {
15 colorManagers.set(manager.key, manager);
16}
17function 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}
37export 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}
57export 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}
77export function colorToHsl(color, index, useIndex = true) {
78 const rgb = colorToRgb(color, index, useIndex);
79 return rgb ? rgbToHsl(rgb) : undefined;
80}
81export function rangeColorToHsl(color, index, useIndex = true) {
82 const rgb = rangeColorToRgb(color, index, useIndex);
83 return rgb ? rgbToHsl(rgb) : undefined;
84}
85export 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}
109export function stringToAlpha(input) {
110 return stringToRgba(input)?.a;
111}
112export function stringToRgb(input) {
113 return stringToRgba(input);
114}
115export 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}
145export 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}
154export 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}
162export function getStyleFromRgb(color, opacity) {
163 const defaultOpacity = 1;
164 return `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity ?? defaultOpacity})`;
165}
166export function getStyleFromHsl(color, opacity) {
167 const defaultOpacity = 1;
168 return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${opacity ?? defaultOpacity})`;
169}
170export 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}
184export 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}
204export 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}
226export 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}
235export 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}
257function 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}
279export 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}
327export 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}