@mytec: iter8 ready for test
This commit is contained in:
117
frontend/src/utils/colorGradient.ts
Normal file
117
frontend/src/utils/colorGradient.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* RSRP → color mapping with smooth gradient interpolation.
|
||||
*
|
||||
* Gradient stops are chosen to match standard RF planning tools:
|
||||
* -130 dBm = deep blue (no service)
|
||||
* -90 dBm = green (fair)
|
||||
* -50 dBm = red (excellent)
|
||||
*
|
||||
* All functions are pure and allocation-free on the hot path
|
||||
* (pre-built lookup table for fast per-pixel color resolution).
|
||||
*/
|
||||
|
||||
interface GradientStop {
|
||||
value: number; // normalized 0–1
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
const GRADIENT_STOPS: GradientStop[] = [
|
||||
{ value: 0.0, r: 26, g: 35, b: 126 }, // #1a237e — deep blue
|
||||
{ value: 0.15, r: 13, g: 71, b: 161 }, // #0d47a1
|
||||
{ value: 0.25, r: 33, g: 150, b: 243 }, // #2196f3 — blue
|
||||
{ value: 0.35, r: 0, g: 188, b: 212 }, // #00bcd4 — cyan
|
||||
{ value: 0.45, r: 0, g: 137, b: 123 }, // #00897b — teal
|
||||
{ value: 0.55, r: 76, g: 175, b: 80 }, // #4caf50 — green
|
||||
{ value: 0.65, r: 139, g: 195, b: 74 }, // #8bc34a — light green
|
||||
{ value: 0.75, r: 255, g: 235, b: 59 }, // #ffeb3b — yellow
|
||||
{ value: 0.85, r: 255, g: 152, b: 0 }, // #ff9800 — orange
|
||||
{ value: 1.0, r: 244, g: 67, b: 54 }, // #f44336 — red
|
||||
];
|
||||
|
||||
/**
|
||||
* Pre-built 256-entry lookup table for O(1) color resolution.
|
||||
* Each entry is [r, g, b].
|
||||
*/
|
||||
const COLOR_LUT: Uint8Array = buildColorLUT();
|
||||
|
||||
function buildColorLUT(): Uint8Array {
|
||||
const size = 256;
|
||||
const lut = new Uint8Array(size * 3);
|
||||
|
||||
for (let i = 0; i < size; i++) {
|
||||
const value = i / (size - 1); // 0–1
|
||||
const [r, g, b] = interpolateGradient(value);
|
||||
lut[i * 3] = r;
|
||||
lut[i * 3 + 1] = g;
|
||||
lut[i * 3 + 2] = b;
|
||||
}
|
||||
|
||||
return lut;
|
||||
}
|
||||
|
||||
function interpolateGradient(value: number): [number, number, number] {
|
||||
// Find surrounding stops
|
||||
let lower = GRADIENT_STOPS[0];
|
||||
let upper = GRADIENT_STOPS[GRADIENT_STOPS.length - 1];
|
||||
|
||||
for (let i = 0; i < GRADIENT_STOPS.length - 1; i++) {
|
||||
if (value >= GRADIENT_STOPS[i].value && value <= GRADIENT_STOPS[i + 1].value) {
|
||||
lower = GRADIENT_STOPS[i];
|
||||
upper = GRADIENT_STOPS[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const range = upper.value - lower.value;
|
||||
const t = range > 0 ? (value - lower.value) / range : 0;
|
||||
|
||||
return [
|
||||
Math.round(lower.r + (upper.r - lower.r) * t),
|
||||
Math.round(lower.g + (upper.g - lower.g) * t),
|
||||
Math.round(lower.b + (upper.b - lower.b) * t),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize RSRP (dBm) to 0–1 range.
|
||||
*
|
||||
* -130 dBm → 0.0
|
||||
* -50 dBm → 1.0
|
||||
*/
|
||||
export function normalizeRSRP(rsrp: number): number {
|
||||
const minRSRP = -130;
|
||||
const maxRSRP = -50;
|
||||
const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP);
|
||||
return Math.max(0, Math.min(1, normalized));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast color lookup from normalized value (0–1).
|
||||
* Uses pre-built LUT — O(1), no allocations.
|
||||
*
|
||||
* Returns [r, g, b] (0–255 each).
|
||||
*/
|
||||
export function valueToColorRGB(value: number): [number, number, number] {
|
||||
const idx = Math.max(0, Math.min(255, Math.round(value * 255))) * 3;
|
||||
return [COLOR_LUT[idx], COLOR_LUT[idx + 1], COLOR_LUT[idx + 2]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Full gradient interpolation (slower, for non-hot-path use like legends).
|
||||
*/
|
||||
export function valueToColor(value: number): [number, number, number] {
|
||||
return interpolateGradient(Math.max(0, Math.min(1, value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaussian falloff for smooth point edges.
|
||||
*
|
||||
* Returns 0–1 weight based on distance from point center.
|
||||
* sigma defaults to radius/3 for a natural falloff.
|
||||
*/
|
||||
export function gaussianWeight(distance: number, radius: number, sigma?: number): number {
|
||||
const s = sigma ?? radius / 3;
|
||||
return Math.exp(-(distance * distance) / (2 * s * s));
|
||||
}
|
||||
Reference in New Issue
Block a user