Add visible-spectrum RGB estimate to blackbody chart
This commit is contained in:
@@ -135,6 +135,17 @@ require __DIR__ . '/header.php';
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bb-color-chip {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.2);
|
||||||
|
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.22);
|
||||||
|
background: rgba(255,255,255,0.08);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.bb-stat-sub {
|
.bb-stat-sub {
|
||||||
color: var(--text-dim);
|
color: var(--text-dim);
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@@ -256,8 +267,9 @@ require __DIR__ . '/header.php';
|
|||||||
</div>
|
</div>
|
||||||
<div class="bb-stat">
|
<div class="bb-stat">
|
||||||
<span class="bb-stat-label">Relative Farbwirkung</span>
|
<span class="bb-stat-label">Relative Farbwirkung</span>
|
||||||
|
<span class="bb-color-chip" id="bbColorChip" aria-hidden="true"></span>
|
||||||
<span class="bb-stat-value" id="bbColorLabel">–</span>
|
<span class="bb-stat-value" id="bbColorLabel">–</span>
|
||||||
<span class="bb-stat-sub">grob aus dem Peak-Bereich</span>
|
<span class="bb-stat-sub" id="bbColorMeta">aus dem sichtbaren Spektrum</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -302,6 +314,8 @@ require __DIR__ . '/header.php';
|
|||||||
const peakRegionOutput = document.getElementById('bbPeakRegion');
|
const peakRegionOutput = document.getElementById('bbPeakRegion');
|
||||||
const fluxOutput = document.getElementById('bbFlux');
|
const fluxOutput = document.getElementById('bbFlux');
|
||||||
const colorLabelOutput = document.getElementById('bbColorLabel');
|
const colorLabelOutput = document.getElementById('bbColorLabel');
|
||||||
|
const colorChipOutput = document.getElementById('bbColorChip');
|
||||||
|
const colorMetaOutput = document.getElementById('bbColorMeta');
|
||||||
const canvas = document.getElementById('bb-canvas');
|
const canvas = document.getElementById('bb-canvas');
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
@@ -399,6 +413,100 @@ require __DIR__ . '/header.php';
|
|||||||
return 'rot bis infrarotdominiert';
|
return 'rot bis infrarotdominiert';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wavelengthToLinearRgb(wavelengthNm) {
|
||||||
|
let red = 0;
|
||||||
|
let green = 0;
|
||||||
|
let blue = 0;
|
||||||
|
|
||||||
|
if (wavelengthNm >= 380 && wavelengthNm < 440) {
|
||||||
|
red = -(wavelengthNm - 440) / 60;
|
||||||
|
blue = 1;
|
||||||
|
} else if (wavelengthNm < 490) {
|
||||||
|
green = (wavelengthNm - 440) / 50;
|
||||||
|
blue = 1;
|
||||||
|
} else if (wavelengthNm < 510) {
|
||||||
|
green = 1;
|
||||||
|
blue = -(wavelengthNm - 510) / 20;
|
||||||
|
} else if (wavelengthNm < 580) {
|
||||||
|
red = (wavelengthNm - 510) / 70;
|
||||||
|
green = 1;
|
||||||
|
} else if (wavelengthNm < 645) {
|
||||||
|
red = 1;
|
||||||
|
green = -(wavelengthNm - 645) / 65;
|
||||||
|
} else if (wavelengthNm <= 780) {
|
||||||
|
red = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let factor = 0;
|
||||||
|
if (wavelengthNm >= 380 && wavelengthNm < 420) {
|
||||||
|
factor = 0.3 + 0.7 * ((wavelengthNm - 380) / 40);
|
||||||
|
} else if (wavelengthNm <= 700) {
|
||||||
|
factor = 1;
|
||||||
|
} else if (wavelengthNm <= 780) {
|
||||||
|
factor = 0.3 + 0.7 * ((780 - wavelengthNm) / 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
r: red * factor,
|
||||||
|
g: green * factor,
|
||||||
|
b: blue * factor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toSrgbChannel(value) {
|
||||||
|
const clamped = clamp(value, 0, 1);
|
||||||
|
if (clamped <= 0.0031308) {
|
||||||
|
return 12.92 * clamped;
|
||||||
|
}
|
||||||
|
return 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
|
||||||
|
}
|
||||||
|
|
||||||
|
function estimateVisibleRgb(temperature) {
|
||||||
|
let red = 0;
|
||||||
|
let green = 0;
|
||||||
|
let blue = 0;
|
||||||
|
|
||||||
|
for (let wavelengthNm = 380; wavelengthNm <= 780; wavelengthNm += 5) {
|
||||||
|
const radiance = planckSpectralRadiance(wavelengthNm * 1e-9, temperature);
|
||||||
|
const rgb = wavelengthToLinearRgb(wavelengthNm);
|
||||||
|
red += radiance * rgb.r;
|
||||||
|
green += radiance * rgb.g;
|
||||||
|
blue += radiance * rgb.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxChannel = Math.max(red, green, blue, 1e-12);
|
||||||
|
return {
|
||||||
|
r: Math.round(toSrgbChannel(red / maxChannel) * 255),
|
||||||
|
g: Math.round(toSrgbChannel(green / maxChannel) * 255),
|
||||||
|
b: Math.round(toSrgbChannel(blue / maxChannel) * 255)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgbToHex(rgb) {
|
||||||
|
return '#' + [rgb.r, rgb.g, rgb.b].map(function (channel) {
|
||||||
|
return channel.toString(16).padStart(2, '0').toUpperCase();
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeVisibleRgb(rgb) {
|
||||||
|
if (rgb.r > 245 && rgb.g > 235 && rgb.b > 235) {
|
||||||
|
return 'neutralweiß';
|
||||||
|
}
|
||||||
|
if (rgb.r > 245 && rgb.g > 220 && rgb.b < 205) {
|
||||||
|
return 'warmweiß';
|
||||||
|
}
|
||||||
|
if (rgb.b > rgb.r + 25 && rgb.b > rgb.g + 10) {
|
||||||
|
return 'kühlbläulich';
|
||||||
|
}
|
||||||
|
if (rgb.r > rgb.b + 25 && rgb.g > rgb.b) {
|
||||||
|
return 'warmgelblich';
|
||||||
|
}
|
||||||
|
if (rgb.r > 235 && rgb.b > 235) {
|
||||||
|
return 'weiß mit Magenta-Stich';
|
||||||
|
}
|
||||||
|
return 'leicht getöntes Weiß';
|
||||||
|
}
|
||||||
|
|
||||||
function drawVisibleBand(plotLeft, plotTop, plotWidth, plotHeight, xMin, xMax) {
|
function drawVisibleBand(plotLeft, plotTop, plotWidth, plotHeight, xMin, xMax) {
|
||||||
const visibleStart = Math.max(380, xMin);
|
const visibleStart = Math.max(380, xMin);
|
||||||
const visibleEnd = Math.min(750, xMax);
|
const visibleEnd = Math.min(750, xMax);
|
||||||
@@ -431,11 +539,14 @@ require __DIR__ . '/header.php';
|
|||||||
|
|
||||||
const peakNm = WIEN_DISPLACEMENT_NM_K / temperature;
|
const peakNm = WIEN_DISPLACEMENT_NM_K / temperature;
|
||||||
const totalFlux = STEFAN_BOLTZMANN * Math.pow(temperature, 4);
|
const totalFlux = STEFAN_BOLTZMANN * Math.pow(temperature, 4);
|
||||||
|
const visibleRgb = estimateVisibleRgb(temperature);
|
||||||
|
|
||||||
peakNmOutput.textContent = formatGermanNumber(peakNm, peakNm < 100 ? 1 : 0) + ' nm';
|
peakNmOutput.textContent = formatGermanNumber(peakNm, peakNm < 100 ? 1 : 0) + ' nm';
|
||||||
peakRegionOutput.textContent = describePeakRegion(peakNm);
|
peakRegionOutput.textContent = describePeakRegion(peakNm);
|
||||||
fluxOutput.textContent = formatScientific(totalFlux) + ' W/m²';
|
fluxOutput.textContent = formatScientific(totalFlux) + ' W/m²';
|
||||||
colorLabelOutput.textContent = describeColor(peakNm);
|
colorLabelOutput.textContent = describeVisibleRgb(visibleRgb);
|
||||||
|
colorChipOutput.style.backgroundColor = 'rgb(' + visibleRgb.r + ', ' + visibleRgb.g + ', ' + visibleRgb.b + ')';
|
||||||
|
colorMetaOutput.textContent = rgbToHex(visibleRgb) + ' | Peak: ' + describeColor(peakNm);
|
||||||
|
|
||||||
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
const ratio = Math.min(window.devicePixelRatio || 1, 2);
|
||||||
const width = canvas.clientWidth;
|
const width = canvas.clientWidth;
|
||||||
|
|||||||
Reference in New Issue
Block a user