Module:Sandbox/AlexNB/nmColor

local p = {}

local redvalue = 0 local greenvalue = 0 local bluevalue = 0 local function nm2RGB(wavelength, IFcorrect) -- code based on an algorithm from Dan Bruton's Color Science Page: http://www.physics.sfasu.edu/astro/color/spectra.html -- calculating RGB color components if (wavelength >= 380) and (wavelength < 440) then redvalue = (440 - wavelength) / 90 greenvalue = 0 bluevalue = 1 elseif (wavelength >= 440) and (wavelength < 490) then redvalue = 0 greenvalue = (wavelength - 440) / 50 bluevalue = 1 elseif (wavelength >= 490) and (wavelength < 510) then redvalue = 0 greenvalue = 1 bluevalue = (510 - wavelength) / 20 elseif (wavelength >= 510) and (wavelength < 580) then redvalue = (wavelength - 510) / 70 greenvalue = 1 bluevalue = 0 elseif (wavelength >= 580) and (wavelength < 645) then redvalue = 1 greenvalue = (645 - wavelength) / 65 bluevalue = 0 elseif (wavelength >= 645) and (wavelength <= 780) then redvalue = 1 greenvalue = 0 bluevalue = 0 end -- calculating intensity correction factor if IFcorrect then local intensityfactor = 0 if (wavelength >= 380) and (wavelength < 420) then intensityfactor = 0.3 + 0.7*(wavelength - 350) / 70 elseif (wavelength >= 420) and (wavelength <= 700) then intensityfactor = 1 elseif (wavelength > 700) and (wavelength <= 780) then intensityfactor = 0.3 + 0.7*(780 - wavelength) / 80 end redvalue = redvalue * intensityfactor greenvalue = greenvalue * intensityfactor bluevalue = bluevalue * intensityfactor end end

function p.emission(frame) -- this function returns the string "#RRGGBB" with approximate RGB value for a wavelength passed as a first argument local wavelength = tonumber(frame.args[1]) nm2RGB(wavelength, true) local result='#' .. string.format("%.2X%.2X%.2X", 255*redvalue, 255*greenvalue, 255*bluevalue) return result end

local function absorption_to_visible(wavelength_abs) -- code based on a color wheel drawn at: http://www2.chemistry.msu.edu/faculty/reusch/virttxtjml/spectrpy/uv-vis/spectrum.htm local wavelength_vis local minVioletIndigo = 380 -- originally, it was 400 local maxVioletIndigo = 430 local widthVioletIndigo = maxVioletIndigo - minVioletIndigo local minBlue = 430 local maxBlue = 490 local widthBlue = maxBlue - minBlue local minGreen = 490 local maxGreen = 560 local widthGreen = maxGreen - minGreen local minYellow = 560 local maxYellow = 580 local widthYellow = maxYellow - minYellow local minOrange = 580 local maxOrange = 650 -- originally, it was 620 local widthOrange = maxOrange - minOrange local minRed = 650 -- originally, it was 620 local maxRed = 780 -- originally, it was 800 local widthRed = maxRed - minRed if (wavelength_abs>=minVioletIndigo) and (wavelength_abs<=maxVioletIndigo) then wavelength_vis = (wavelength_abs - minVioletIndigo)*widthYellow/widthVioletIndigo + minYellow elseif (wavelength_abs>minBlue) and (wavelength_abs<=maxBlue) then wavelength_vis = (wavelength_abs - minBlue)*widthOrange/widthBlue + minOrange elseif (wavelength_abs>minGreen) and (wavelength_abs<=maxGreen) then wavelength_vis = (wavelength_abs - minGreen)*widthRed/widthGreen + minRed elseif (wavelength_abs>minYellow) and (wavelength_abs<=maxYellow) then wavelength_vis = (wavelength_abs - minYellow)*widthVioletIndigo/widthYellow + minVioletIndigo elseif (wavelength_abs>minOrange) and (wavelength_abs<=maxOrange) then wavelength_vis = (wavelength_abs - minOrange)*widthBlue/widthOrange + minBlue elseif (wavelength_abs>minRed) and (wavelength_abs<=maxRed) then wavelength_vis = (wavelength_abs - minRed)*widthGreen/(widthRed + 20) + minGreen -- one of the "corrected" versions: wavelength_vis = (wavelength_abs - minRed - 30)*widthGreen/widthRed + minGreen end return wavelength_vis end

local minAntiMagenta = 550 local maxAntiMagenta = 570

local function process_magenta(wavelength_abs) local redvalue_RED local bluevalue_RED local greenvalue_RED local redvalue_BLUE local bluevalue_BLUE local greenvalue_BLUE local widthAntiMagenta = maxAntiMagenta - minAntiMagenta local bias_BLUE = (wavelength_abs - minAntiMagenta)/widthAntiMagenta local bias_RED = (maxAntiMagenta - wavelength_abs)/widthAntiMagenta nm2RGB(700, false) redvalue_RED = redvalue*bias_RED bluevalue_RED = bluevalue*bias_RED greenvalue_RED = greenvalue*bias_RED nm2RGB(400, false) redvalue_BLUE = redvalue*bias_BLUE bluevalue_BLUE = bluevalue*bias_BLUE greenvalue_BLUE = greenvalue*bias_BLUE redvalue = 0.6+ 0.4*(redvalue_RED + redvalue_BLUE) bluevalue = 0.7 + 0.3*(bluevalue_RED + bluevalue_BLUE) greenvalue = greenvalue_RED + greenvalue_BLUE end

function p.absorption(frame) -- this function returns the string "#RRGGBB" with approximate RGB value for a complementary color (reflection) to a color, defined as a wavelength passed as a first argument local wavelength = tonumber(frame.args[1]) -- adding magentas to smoothen red->blue transition. Trying 540..570 nm   if (wavelength>=minAntiMagenta) and (wavelength<=maxAntiMagenta) then process_magenta(wavelength) else nm2RGB(absorption_to_visible(wavelength), false) end local result='#' .. string.format("%.2X%.2X%.2X", 255*redvalue, 255*greenvalue, 255*bluevalue) return result end

return p