add colorblind setting and friendly palettes

- mode applies to plot data, plot background color, statsseries colors, roofline timescale
This commit is contained in:
Christoph Kluge 2025-01-14 17:40:25 +01:00
parent 42e8e37bd4
commit 736236e9ca
5 changed files with 125 additions and 16 deletions

View File

@ -18,6 +18,7 @@
const ccconfig = getContext("cc-config"); const ccconfig = getContext("cc-config");
let message = { msg: "", target: "", color: "#d63384" }; let message = { msg: "", target: "", color: "#d63384" };
let displayMessage = false; let displayMessage = false;
let cbmode = ccconfig?.plot_general_colorblindMode || false;
async function handleSettingSubmit(event) { async function handleSettingSubmit(event) {
const selector = event.detail.selector const selector = event.detail.selector
@ -28,6 +29,9 @@
const res = await fetch(form.action, { method: "POST", body: formData }); const res = await fetch(form.action, { method: "POST", body: formData });
if (res.ok) { if (res.ok) {
let text = await res.text(); let text = await res.text();
if (formData.get("key") === "plot_general_colorblindMode") {
cbmode = JSON.parse(formData.get("value"));
}
popMessage(text, target, "#048109"); popMessage(text, target, "#048109");
} else { } else {
let text = await res.text(); let text = await res.text();
@ -51,4 +55,4 @@
<UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/> <UserOptions config={ccconfig} {username} {isApi} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
<PlotRenderOptions config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/> <PlotRenderOptions config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>
<PlotColorScheme config={ccconfig} bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/> <PlotColorScheme config={ccconfig} bind:cbmode bind:message bind:displayMessage on:update-config={(e) => handleSettingSubmit(e)}/>

View File

@ -24,6 +24,7 @@
export let config; export let config;
export let message; export let message;
export let displayMessage; export let displayMessage;
export let cbmode = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function updateSetting(selector, target) { function updateSetting(selector, target) {
@ -265,6 +266,62 @@
], ],
}; };
// https://personal.sron.nl/~pault/
// https://tsitsul.in/blog/coloropt/
const cvdschemes = {
HighContrast: [
"rgb(221,170,51)",
"rgb(187,85,102)",
"rgb(0,68,136)",
"rgb(0,0,0)",
],
Bright: [
"rgb(68,119,170)",
"rgb(102,204,238)",
"rgb(34,136,51)",
"rgb(204,187,68)",
"rgb(238,102,119)",
"rgb(170,51,119)",
"rgb(187,187,187)",
],
Muted: [
"rgb(51,34,136)",
"rgb(136,204,238)",
"rgb(68,170,153)",
"rgb(17,119,51)",
"rgb(153,153,51)",
"rgb(221,204,119)",
"rgb(204,102,119)",
"rgb(136,34,85)",
"rgb(170,68,153)",
"rgb(221,221,221)",
],
NormalSixColor: [
"rgb(64,83,211)",
"rgb(221,179,16)",
"rgb(181,29,20)",
"rgb(0,190,255)",
"rgb(251,73,176)",
"rgb(0,178,93)",
"rgb(202,202,202)",
],
NormalTwelveColor: [
"rgb(235,172,35)",
"rgb(184,0,88)",
"rgb(0,140,249)",
"rgb(0,110,0)",
"rgb(0,187,173)",
"rgb(209,99,230)",
"rgb(178,69,2)",
"rgb(255,146,135)",
"rgb(89,84,214)",
"rgb(0,198,248)",
"rgb(135,133,0)",
"rgb(0,167,108)",
"rgb(189,189,189)",
]
}
</script> </script>
<Row cols={1} class="p-2 g-2"> <Row cols={1} class="p-2 g-2">
@ -281,7 +338,7 @@
<CardTitle <CardTitle
style="margin-bottom: 1em; display: flex; align-items: center;" style="margin-bottom: 1em; display: flex; align-items: center;"
> >
<div>Color Scheme for Timeseries Plots</div> <div>Color Scheme for Timeseries Plots {cbmode ? `(Color Blind Friendly Palettes)` : ``}</div>
{#if displayMessage && message.target == "cs"}<div {#if displayMessage && message.target == "cs"}<div
style="margin-left: auto; font-size: 0.9em;" style="margin-left: auto; font-size: 0.9em;"
> >
@ -293,7 +350,7 @@
<input type="hidden" name="key" value="plot_general_colorscheme" /> <input type="hidden" name="key" value="plot_general_colorscheme" />
<Table hover> <Table hover>
<tbody> <tbody>
{#each Object.entries(colorschemes) as [name, rgbrow]} {#each Object.entries(cbmode ? cvdschemes : colorschemes) as [name, rgbrow]}
<tr> <tr>
<th scope="col">{name}</th> <th scope="col">{name}</th>
<td> <td>
@ -333,8 +390,9 @@
<style> <style>
.color-dot { .color-dot {
height: 10px; margin-left: 1px;
width: 10px; height: 12px;
width: 12px;
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
} }

View File

@ -129,8 +129,8 @@
> >
<!-- BACKGROUND --> <!-- BACKGROUND -->
<Col <Col class="d-flex justify-content-between"
><Card class="h-100"> ><Card class="h-100" style="width: 49%;">
<form <form
id="backgrounds-form" id="backgrounds-form"
method="post" method="post"
@ -173,6 +173,50 @@
</div> </div>
<Button color="primary" type="submit">Submit</Button> <Button color="primary" type="submit">Submit</Button>
</form> </form>
</Card></Col </Card>
> <Card class="h-100" style="width: 49%;">
<form
id="colorblindmode-form"
method="post"
action="/frontend/configuration/"
class="card-body"
on:submit|preventDefault={() =>
updateSetting("#colorblindmode-form", "cbm")}
>
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->
<CardTitle
style="margin-bottom: 1em; display: flex; align-items: center;"
>
<div>Color Blind Mode</div>
{#if displayMessage && message.target == "cbm"}<div
style="margin-left: auto; font-size: 0.9em;"
>
<code style="color: {message.color};" out:fade
>Update: {message.msg}</code
>
</div>{/if}
</CardTitle>
<input type="hidden" name="key" value="plot_general_colorblindMode" />
<div class="mb-3">
<div>
{#if config?.plot_general_colorblindMode}
<input type="radio" id="cbm-true-checked" name="value" value="true" checked />
{:else}
<input type="radio" id="cbm-true" name="value" value="true" />
{/if}
<label for="true">Yes</label>
</div>
<div>
{#if config?.plot_general_colorblindMode}
<input type="radio" id="cbm-false" name="value" value="false" />
{:else}
<input type="radio" id="cbm-false-checked" name="value" value="false" checked />
{/if}
<label for="false">No</label>
</div>
</div>
<Button color="primary" type="submit">Submit</Button>
</form>
</Card>
</Col>
</Row> </Row>

View File

@ -152,10 +152,12 @@
const lineWidth = const lineWidth =
clusterCockpitConfig.plot_general_lineWidth / window.devicePixelRatio; clusterCockpitConfig.plot_general_lineWidth / window.devicePixelRatio;
const lineColors = clusterCockpitConfig.plot_general_colorscheme; const lineColors = clusterCockpitConfig.plot_general_colorscheme;
const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false;
const backgroundColors = { const backgroundColors = {
normal: "rgba(255, 255, 255, 1.0)", normal: "rgba(255, 255, 255, 1.0)",
caution: "rgba(255, 128, 0, 0.3)", caution: cbmode ? "rgba(239, 230, 69, 0.3)" : "rgba(255, 128, 0, 0.3)",
alert: "rgba(255, 0, 0, 0.3)", alert: cbmode ? "rgba(225, 86, 44, 0.3)" : "rgba(255, 0, 0, 0.3)",
}; };
const thresholds = findThresholds( const thresholds = findThresholds(
subClusterTopology, subClusterTopology,
@ -346,13 +348,13 @@
label: "min", label: "min",
scale: "y", scale: "y",
width: lineWidth, width: lineWidth,
stroke: "red", stroke: cbmode ? "rgb(0,255,0)" : "red",
}); });
plotSeries.push({ plotSeries.push({
label: "max", label: "max",
scale: "y", scale: "y",
width: lineWidth, width: lineWidth,
stroke: "green", stroke: cbmode ? "rgb(0,0,255)" : "green",
}); });
plotSeries.push({ plotSeries.push({
label: usesMeanStatsSeries ? "mean" : "median", label: usesMeanStatsSeries ? "mean" : "median",
@ -362,8 +364,8 @@
}); });
plotBands = [ plotBands = [
{ series: [2, 3], fill: "rgba(0,255,0,0.1)" }, { series: [2, 3], fill: cbmode ? "rgba(0,0,255,0.1)" : "rgba(0,255,0,0.1)" },
{ series: [3, 1], fill: "rgba(255,0,0,0.1)" }, { series: [3, 1], fill: cbmode ? "rgba(0,255,0,0.1)" : "rgba(255,0,0,0.1)" },
]; ];
} else { } else {
for (let i = 0; i < series.length; i++) { for (let i = 0; i < series.length; i++) {

View File

@ -40,6 +40,7 @@
let timeoutId = null; let timeoutId = null;
const lineWidth = clusterCockpitConfig.plot_general_lineWidth; const lineWidth = clusterCockpitConfig.plot_general_lineWidth;
const cbmode = clusterCockpitConfig?.plot_general_colorblindMode || false;
// Helpers // Helpers
function getGradientR(x) { function getGradientR(x) {
@ -61,7 +62,7 @@
return Math.floor(x * 255.0); return Math.floor(x * 255.0);
} }
function getRGB(c) { function getRGB(c) {
return `rgb(${getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})`; return `rgb(${cbmode ? '0' : getGradientR(c)}, ${getGradientG(c)}, ${getGradientB(c)})`;
} }
function nearestThousand(num) { function nearestThousand(num) {
return Math.ceil(num / 1000) * 1000; return Math.ceil(num / 1000) * 1000;