more testing and cleanup

This commit is contained in:
Paul Marbach
2025-12-17 17:42:35 -05:00
parent 2c9b7ca135
commit c7af2be682
2 changed files with 27 additions and 15 deletions
@@ -151,6 +151,19 @@ describe('RadialGauge color utils', () => {
expect(colorAtGradientPercent(gradient, 1).toHexString()).toBe('#0000ff');
});
it('will still work if unsorted', () => {
const gradient = [
{ color: '#0000ff', percent: 1 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#ff0000', percent: 0 },
];
expect(colorAtGradientPercent(gradient, 0).toHexString()).toBe('#ff0000');
expect(colorAtGradientPercent(gradient, 0.25).toHexString()).toBe('#808000');
expect(colorAtGradientPercent(gradient, 0.5).toHexString()).toBe('#00ff00');
expect(colorAtGradientPercent(gradient, 0.75).toHexString()).toBe('#008080');
expect(colorAtGradientPercent(gradient, 1).toHexString()).toBe('#0000ff');
});
it('should not throw an error when percent is outside 0-1 range', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
@@ -102,7 +102,7 @@ export function colorAtGradientPercent(stops: GradientStop[], percent: number):
throw new Error('colorAtGradientPercent requires at least two color stops');
}
// normalize and sort stops by percent
// normalize and sort stops by percent. TODO: is this necessary? is gradientstops always sorted?
const sorted = stops
.map((s) => ({ color: s.color, percent: clamp(s.percent, 0, 1) }))
.sort((a, b) => a.percent - b.percent);
@@ -115,25 +115,24 @@ export function colorAtGradientPercent(stops: GradientStop[], percent: number):
return tinycolor(sorted[sorted.length - 1].color);
}
// find surrounding stops
let left = sorted[0];
let right = sorted[sorted.length - 1];
for (let i = 1; i < sorted.length; i++) {
if (percent <= sorted[i].percent) {
left = sorted[i - 1];
right = sorted[i];
break;
// find surrounding stops using binary search
let lo = 0;
let hi = sorted.length - 1;
while (lo + 1 < hi) {
const mid = (lo + hi) >> 1;
if (percent <= sorted[mid].percent) {
hi = mid;
} else {
lo = mid;
}
}
const left = sorted[lo];
const right = sorted[hi];
const range = right.percent - left.percent;
const t = range === 0 ? 0 : (percent - left.percent) / range; // 0..1
// tinycolor.mix expects amount as percentage of the second color
const mixed = tinycolor.mix(left.color, right.color, t * 100);
// return hex6 if opaque, hex8 if has alpha
return mixed;
return tinycolor.mix(left.color, right.color, t * 100);
}
export function getBarEndcapColors(gradientStops: GradientStop[], percent = 1): [string, string] {