package org.sunflow.image;

/**
 * This class allows spectral curves to be defined from irregularly sampled
 * data. Note that the wavelength array is assumed to be sorted low to high. Any
 * values beyond the defined range will simply be extended to infinity from the
 * end points. Points inside the valid range will be linearly interpolated
 * between the two nearest samples. No explicit error checking is performed, but
 * this class will run into {@link ArrayIndexOutOfBoundsException}s if the
 * array lengths don't match.
 */
public class IrregularSpectralCurve extends SpectralCurve {
    private final float[] wavelengths;
    private final float[] amplitudes;

    /**
     * Define an irregular spectral curve from the provided (sorted) wavelengths
     * and amplitude data. The wavelength array is assumed to contain values in
     * nanometers. Array lengths must match.
     * 
     * @param wavelengths sampled wavelengths in nm
     * @param amplitudes amplitude of the curve at the sampled points
     */
    public IrregularSpectralCurve(float[] wavelengths, float[] amplitudes) {
        this.wavelengths = wavelengths;
        this.amplitudes = amplitudes;
        if (wavelengths.length != amplitudes.length)
            throw new RuntimeException(String.format("Error creating irregular spectral curve: %d wavelengths and %d amplitudes", wavelengths.length, amplitudes.length));
        for (int i = 1; i < wavelengths.length; i++)
            if (wavelengths[i - 1] >= wavelengths[i])
                throw new RuntimeException(String.format("Error creating irregular spectral curve: values are not sorted - error at index %d", i));
    }

    @Override
    public float sample(float lambda) {
        if (wavelengths.length == 0)
            return 0; // no data
        if (wavelengths.length == 1 || lambda <= wavelengths[0])
            return amplitudes[0];
        if (lambda >= wavelengths[wavelengths.length - 1])
            return amplitudes[wavelengths.length - 1];
        for (int i = 1; i < wavelengths.length; i++) {
            if (lambda < wavelengths[i]) {
                float dx = (lambda - wavelengths[i - 1]) / (wavelengths[i] - wavelengths[i - 1]);
                return (1 - dx) * amplitudes[i - 1] + dx * amplitudes[i];
            }
        }
        return amplitudes[wavelengths.length - 1];
    }
}