/*
 * Copyright (C) 2007 askywhale
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License (version 2)
 *  as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 */

package com.askywhale.rings;

import net.sf.j3d.util.graphics.RGB;
import net.sf.j3d.util.Vector;

/**
 * Rings Raytracer related class
 * 
 * Permit gauss blurred isosurfaces (each point is an average
 * of points around, add curvyness to straight forms)
 * 
 * exactvalue is the replacement for value
 * 
 * @author askywhale
 *
 */
public abstract class BlurredIsoSurface extends IsoSurface {

	public final double DEFAULT_BLUR_SIGMA = .01;
	public final double DEFAULT_MLT_X_ON_SIGMA = 1.3; //>1 
	public final int DEFAULT_Q_BLUR_PRECISION = 2;
	public final int DEFAULT_Q_BLUR_INV_MIN_MLT = 8;
	
	private double blurSigma, qBlurInvMinMlt;
	private int qBlurPrecision;
	
    public BlurredIsoSurface(Vector location, double size) {
        super(location, size);
        blurSigma = DEFAULT_BLUR_SIGMA;
        qBlurPrecision = DEFAULT_Q_BLUR_PRECISION;
        qBlurInvMinMlt = DEFAULT_Q_BLUR_INV_MIN_MLT;
    }
    
    public BlurredIsoSurface(Vector location, double size, RGB color) {
        super(location, size, color);
        blurSigma = DEFAULT_BLUR_SIGMA;
        qBlurPrecision = DEFAULT_Q_BLUR_PRECISION;
        qBlurInvMinMlt = DEFAULT_Q_BLUR_INV_MIN_MLT; 
    }
    
	public double getBlurSigma() {
		return blurSigma;
	}
	/**
	 * @param blurSigma : distance of blur (gauss calculation); 
	 * default to .01
	 */
	public void setBlurSigma(double blurSigma) {
		this.blurSigma = blurSigma;
	}

	public int getQBlurPrecision() {
		return qBlurPrecision;
	}

	/**
	 * @param qBlurPrecision : (qBlurPrecision*2+1)^3 : number
	 * of points used forcalculation ; 0 = no blur
	 */
	public void setQBlurPrecision(int qBlurPrecision) {
		this.qBlurPrecision = qBlurPrecision;
	}
	
	public double getQBlurInvMinMlt() {
		return qBlurInvMinMlt;
	}

	/**
	 * @param blurInvMinMlt inv thresold of ignored points ; 
	 * default to 8 (i.e. points with a multiplicator of less 
	 * than 1/8 - far points - are ignored) 
	 */
	public void setQBlurInvMinMlt(double blurInvMinMlt) {
		qBlurInvMinMlt = blurInvMinMlt;
	}

	@Override
	protected double getValue(Vector point) {
		double mltTot = 0;
		double vTot = 0;
		double optMlt = 1;
		if(qBlurPrecision>0)
			optMlt = DEFAULT_MLT_X_ON_SIGMA*blurSigma/qBlurPrecision;
		double optInvSigmaSq = 1/(blurSigma*blurSigma);
		for(double nx=-qBlurPrecision;nx<=qBlurPrecision;nx++)
			for(double ny=-qBlurPrecision;ny<=qBlurPrecision;ny++)
				for(double nz=-qBlurPrecision;nz<=qBlurPrecision;nz++) {
					Vector d = new Vector(nx,ny,nz);
					double mlt = 1;
					if(nx!=0||ny!=0||nz!=0) {					
						d = d.multiply(optMlt);
						//	gauss blur
						mlt = Math.exp(-d.lengthSq()*optInvSigmaSq);							
					} 
					if(mlt>1.0/qBlurInvMinMlt) {
						mltTot+=mlt;
						vTot+=mlt*getExactValue(point.add(d));
					}
				}
		return vTot/mltTot;
	}
	
	protected abstract double getExactValue(Vector point);
}
