
package skyview.geometry.sampler;

import skyview.executive.Settings;

/** This class implements a nearest neighbor sampling
  * scheme.
  */
public class Lanczos extends skyview.geometry.Sampler {
    
    /** The kernel of the classic Lanczos sampler is of the
     *  form: 
     *     sinc(pi x) sinc(pi x/n) 
     *  where n is the number order of the sampler.  The
     *  kernel is non-zero for x between -n and n.
     *  The integral of this kernel is not exactly 1 but is given
     *  in the values below.
     *  Thus use of the Lanczos sampler will slightly shift the values mean
     *  of an image.  If the LanczosNorm setting is not given,
     *  then we normalize by this value.
     */
    private static double[] renorm = new double[]{
        1, 
        0.902823335802,
        1.0097898406,
        0.997055,
        1.00126,
        0.999353,
        1.00038,
        0.999762,
        1.00016
    };
    
    
    public String getName() {
	return "Lanczos"+nLobe+" Sampler";
    }
    
    public String getDescription() {
	return "Sample using smoothly truncated sinc kernel";
    }
    
    /** The number of lobes in the window */
    private int nLobe;
    private long nLobeL;
    private double[] out = new double[2];
    
    private double coef, coef2;
    
    /** Weights used internally */
    private double[] xw;
    private double[] yw;
    
    // The normalization to be used.
    private double norm = 1;
    
    /** Create a Lanczos sample of specified width sampler 
     *  The data will be set later.
     *  
     */
    public void setOrder(int n) {
	init(n);
    }
    
    /** Create a three lobe lanczos sampler as a default width.
     */
    public Lanczos() {
	init(3);
    }
    
    
    
    private void init (int n) {
	this.nLobe = n;
        this.nLobeL = n;
	this.coef  = Math.PI/n;
	this.coef2 = coef*Math.PI;
	xw = new double[2*n];
	yw = new double[2*n];
        
        // Slightly renormalize the sampler to keep
        // mean values constant.  We need to use
        // the square of the normalization since we
        // are resampling in two directions.
        if (!Settings.has("LanczosNorm")) {
            if (n > 0 && n < renorm.length) {
                norm = 1/renorm[n]/renorm[n];
            }
        };
    }
    
    /** Sample a single pixel
      * @param pix  The pixel index.
      */
    public void sample(int pix) {
	
	double output = 0;
        double[] in = outImage.getCenter(pix);
	trans.transform(in, out);
	
	double x = out[0]-0.5;
	double y = out[1]-0.5;

        long ix = (int) Math.floor(x);
	long iy = (int) Math.floor(y);
	
	double dx = ix - x - (nLobe-1);
	double dy = iy - y - (nLobe-1);
	
	
	
	if (ix <nLobeL-1L || y < nLobeL-1L || ix >= inWidth-nLobe || iy >= inHeight-nLobe) {
	    return;
	    
	} else {
	    for (int xc=0; xc < 2*nLobe; xc += 1) {
		if (Math.abs(dx) < 1.e-10) {
		    xw[xc] = 1;
		} else {
		    xw[xc] = Math.sin(coef*dx)*Math.sin(Math.PI*dx)/(coef2*dx*dx);
		}
		dx += 1;
	    }
	    
	    for (int yc=0; yc < 2*nLobe; yc += 1) {
		if (Math.abs(dy) < 1.e-10) {
		    yw[yc] = 1;
		} else {
		    yw[yc] = Math.sin(coef*dy)*Math.sin(Math.PI*dy)/(coef2*dy*dy);
		}
		dy += 1;
	    }
	    long p=0,pstart=0;
	    
	    for (int k=0; k<inDepth; k += 1) {
		
		
		p  = (iy-(nLobe-1))*inWidth + ix-(nLobe-1) + k*(long)inWidth*(long)inHeight;
		
		for (int yc=0; yc<2*nLobe; yc += 1) {
		   
		    for (int xc=0; xc<2*nLobe; xc += 1) {
			
			output += inImage.getData(p)*xw[xc]*yw[yc];
			p += 1;
		    }
		    p += inWidth - 2*nLobe;
		}
	    }
	}
        // Normalize output to keep mean constant.
        output *= norm;
	outImage.setData(pix, output);
    }
}

