diff --git a/public/blackbody_radiation.php b/public/blackbody_radiation.php
index de8e774..52e8855 100644
--- a/public/blackbody_radiation.php
+++ b/public/blackbody_radiation.php
@@ -135,6 +135,17 @@ require __DIR__ . '/header.php';
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 {
color: var(--text-dim);
font-size: 0.8rem;
@@ -256,8 +267,9 @@ require __DIR__ . '/header.php';
Relative Farbwirkung
+
–
- grob aus dem Peak-Bereich
+ aus dem sichtbaren Spektrum
@@ -302,6 +314,8 @@ require __DIR__ . '/header.php';
const peakRegionOutput = document.getElementById('bbPeakRegion');
const fluxOutput = document.getElementById('bbFlux');
const colorLabelOutput = document.getElementById('bbColorLabel');
+ const colorChipOutput = document.getElementById('bbColorChip');
+ const colorMetaOutput = document.getElementById('bbColorMeta');
const canvas = document.getElementById('bb-canvas');
const ctx = canvas.getContext('2d');
@@ -399,6 +413,100 @@ require __DIR__ . '/header.php';
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) {
const visibleStart = Math.max(380, xMin);
const visibleEnd = Math.min(750, xMax);
@@ -431,11 +539,14 @@ require __DIR__ . '/header.php';
const peakNm = WIEN_DISPLACEMENT_NM_K / temperature;
const totalFlux = STEFAN_BOLTZMANN * Math.pow(temperature, 4);
+ const visibleRgb = estimateVisibleRgb(temperature);
peakNmOutput.textContent = formatGermanNumber(peakNm, peakNm < 100 ? 1 : 0) + ' nm';
peakRegionOutput.textContent = describePeakRegion(peakNm);
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 width = canvas.clientWidth;