Plotly.js 스펙트럼 파장 그래프 만들어보기
스펙트로미터의 측정한 데이터 파장을 색깔을 카로티노이드와 엽록소처럼 채워서 넣는 그래프로 표현하는 스크립트이다.
<div id="plot"></div>
<script>
// 파장 데이터 (380~700nm, 5nm 간격)
let x = [];
for (let i = 380; i <= 700; i += 5) {
x.push(i);
}
// 엽록소 흡수 데이터 (두 개의 가우시안 곡선: ~430nm, ~660nm)
function chlorophyllAbs(wavelength) {
let peak1 = Math.exp(-Math.pow((wavelength - 430)/20, 2)); // 청색
let peak2 = Math.exp(-Math.pow((wavelength - 660)/30, 2)); // 적색
return 0.9 * peak1 + 1.0 * peak2;
}
// 카로티노이드 흡수 데이터 (~480nm 중심, 녹색-청록)
function carotenoidAbs(wavelength) {
return 0.6 * Math.exp(-Math.pow((wavelength - 480)/40, 2));
}
// 흡수 스펙트럼 생성
let chl = x.map(w => chlorophyllAbs(w));
let car = x.map(w => carotenoidAbs(w));
let total = chl.map((v, i) => v + car[i]);
// 파장 -> 색상 매핑 (RGB 근사)
function wavelengthToRGB(w) {
let r=0, g=0, b=0;
if (w >= 380 && w < 440) {
r = -(w - 440) / (440 - 380);
g = 0.0;
b = 1.0;
} else if (w >= 440 && w < 490) {
r = 0.0;
g = (w - 440) / (490 - 440);
b = 1.0;
} else if (w >= 490 && w < 510) {
r = 0.0;
g = 1.0;
b = -(w - 510) / (510 - 490);
} else if (w >= 510 && w < 580) {
r = (w - 510) / (580 - 510);
g = 1.0;
b = 0.0;
} else if (w >= 580 && w < 645) {
r = 1.0;
g = -(w - 645) / (645 - 580);
b = 0.0;
} else if (w >= 645 && w <= 700) {
r = 1.0;
g = 0.0;
b = 0.0;
}
// 감마 보정
r = Math.pow(r, 0.8);
g = Math.pow(g, 0.8);
b = Math.pow(b, 0.8);
return `rgb(${Math.round(r*255)},${Math.round(g*255)},${Math.round(b*255)})`;
}
// 파장 구간별로 fill trace 생성
let traces = [];
for (let i = 0; i < x.length - 1; i++) {
let avgW = (x[i] + x[i+1]) / 2;
let color = wavelengthToRGB(avgW);
traces.push({
x: [x[i], x[i+1]],
y: [total[i], total[i+1]],
mode: "lines",
fill: "tozeroy",
line: {color: color, width: 2},
fillcolor: color,
hoverinfo: "x+y",
name: `${x[i]}-${x[i+1]} nm`
});
}
let layout = {
title: "Absorption Spectrum of Chlorophyll & Carotenoids",
xaxis: {title: "Wavelength (nm)", range: [380, 700]},
yaxis: {title: "Absorbance", range: [0, 2]},
showlegend: false
};
plot = document.getElementById('plot');
Plotly.newPlot(plot, traces, layout);
</script>