/*
 *  psychlops_g_shader_fig.cpp
 *  Psychlops Standard Library (Universal)
 *
 *  Last Modified 2009/12/14 by Kenchi HOSOKAWA
 *  (C) 2009 Kenchi HOSOKAWA, Kazushi MARUYA and Takao SATO
 */

#include <iostream>
#include <fstream>
#include "../../../core/ApplicationInterfaces/psychlops_app_info.h"
#include "psychlops_g_shader_fig.h"


namespace Psychlops {

		void ShaderInterface::reparseGLSL1_1(const std::string &source, std::string &result, std::vector<std::string> &args)
		{
			int main_i, after_main_i, in_i, in_finish_i;
			main_i = source.find("void main");
			std::string argstr, argtmp;
			int begin=0, len;
			args.clear();
			if( main_i != std::string::npos ) {
				after_main_i = source.find_first_of("{", main_i );
				in_i = source.find("in float ");
				if( in_i != std::string::npos && in_i<main_i ) {
					in_finish_i = source.find_first_of(";", in_i );
					argstr = source.substr(in_i+9, in_finish_i-(in_i+9)+1);
					for(int i=0; i<argstr.length(); i++) {
						if(argstr[i]==',' || argstr[i]==';') {
							len = i-begin;
							argtmp = argstr.substr(begin, len);
							args.push_back(argtmp);
							begin = i+1;
						}
					}
					result = "";
					std::stringstream ss;
					ss << source.substr(0, in_i);
					ss << source.substr(in_finish_i+1, after_main_i-in_finish_i) << std::endl;
					if(!args.empty()) {
						ss << "\tfloat ";
						for(int i=0; i<args.size(); i++) {
							ss << args[i] << " = gl_TexCoord[" << i/4+4 << "][" << i%4 << "]";
							if(i<args.size()-1) ss << ", ";
						}
						ss << ";" << std::endl;
					}
					ss << source.substr(after_main_i+1);
					result.assign(ss.str());
					return;
				}
			}
			result.assign(source);
		}

namespace Internal
{
	std::string error_message;
	void loadGLSL(std::stringstream &buf, const std::string path)
	{
		char one;
		std::ifstream is;
		is.open(path.c_str(), std::ifstream::in);
		if(is.fail()) {
			AppState::alert("Shader: File was not found, or failed to read for some reasons.");
			return;
		}
		while(is.good()) {
			is.get(one);
			buf << one;
		}
	}

	void logError()
	{
		std::ofstream os;
		os.open("GLSLErrorLog.txt");
		if(os.fail()) {
			AppState::alert("Shader: error log file was not found, or failed to read for some reasons.");
			return;
		}
		os << error_message;
	}
}

namespace Figures {


	ShaderImage::~ShaderImage()
	{
	}
	ShaderImage::ShaderImage()
	: api() {
		initialized = false;
		setBase();
		orig_source =
		"void main(void){ "
		"gl_FragColor = getPix(); }";
		arg_tmp[0] = 0; arg_tmp[1] = 0; arg_tmp[2] = 0; arg_tmp[3] = 0;
		arg_tmp[4] = 0; arg_tmp[5] = 0; arg_tmp[6] = 0; arg_tmp[7] = 0;
		arg_tmp[8] = 0; arg_tmp[9] = 0; arg_tmp[10]= 0; arg_tmp[11]= 0;
		arg_tmp[12]= 0; arg_tmp[13]= 0; arg_tmp[14]= 0; arg_tmp[15]= 0;
	}
	void ShaderImage::setBase()
	{
	}
	void ShaderImage::setFunction(const char *source, ShaderInterface::Version reparse)
	{
		std::string tmp(source);
		setFunction(tmp);
	}
	void ShaderImage::setFunction(const std::string &source, ShaderInterface::Version reparse)
	{
		switch(reparse) {
		case ShaderInterface::AUTO_DETECT:
		case ShaderInterface::GLSL1_1:
			ShaderInterface::reparseGLSL1_1(source, orig_source, orig_args);
			break;
		default:
			orig_source = source;
			break;
		}
	}
	void ShaderImage::argf(
		double  a0, double  a1, double  a2, double  a3,
		double  a4, double  a5, double  a6, double  a7,
		double  a8, double  a9, double a10, double a11,
		double a12, double a13, double a14, double a15
	)
	{
		arg_tmp[0] =  a0; arg_tmp[1] =  a1; arg_tmp[2] =  a2; arg_tmp[3] =  a3;
		arg_tmp[4] =  a4; arg_tmp[5] =  a5; arg_tmp[6] =  a6; arg_tmp[7] =  a7;
		arg_tmp[8] =  a8; arg_tmp[9] =  a9; arg_tmp[10]= a10; arg_tmp[11]= a11;
		arg_tmp[12]= a12; arg_tmp[13]= a13; arg_tmp[14]= a14; arg_tmp[15]= a15;
	}

	void ShaderImage::cache(DrawableWithCache &target)
	{
		api.cacheTex(orig_source, orig_args, target);
	}
	void ShaderImage::cache(const char *source, DrawableWithCache &target)
	{
		if(!initialized && &target!=0) {
			setFunction(source);
			cache(target);
			initialized = true;
		}
	}
	void ShaderImage::cacheFromFile(const std::string path, DrawableWithCache &target)
	{
		std::stringstream buf;
		Internal::loadGLSL(buf, path);
		Internal::error_message.clear();
		try{
			setFunction(buf.str().c_str());
			cache(*Display::the_canvas);
		} catch (Exception e) {
			Internal::error_message = e.getErrorString();
			Internal::logError();
		} catch (Exception *e) {
			Internal::error_message = e->getErrorString();
			Internal::logError();
		}
	}
	ShaderImage& ShaderImage::draw(Image &img, const double* argv, const int argn, Canvas &target)
	{
		//if() cache(target);
		api.drawImage(img, argv, argn, target);
		return *this;
	}
	void ShaderImage::to(Image &dest, Image &img, const double* argv, const int argn, Canvas &media)
	{
		Point tmp = img.getDatum();
		api.imageToImage(dest, img, argv, argn, media);
		img.setDatum(tmp);
	}
	ShaderImage& ShaderImage::draw(Image &img, Canvas &target)
	{
		return draw(img, arg_tmp, 4, target);
	}
	void ShaderImage::to(Image &dest, Image &img, Canvas &media)
	{
		to(dest, img, arg_tmp, 4, media);
	}


	ShaderField::~ShaderField()
	{
	}
	ShaderField::ShaderField()
	: api() {
		initialized = false;
		setBase();
		orig_source =
		"void main(void){ "
		"pix(vec4(0.0, 0.0, 1.0, 0.5)); }";
		arg_tmp[0] = 0; arg_tmp[1] = 0; arg_tmp[2] = 0; arg_tmp[3] = 0;
		arg_tmp[4] = 0; arg_tmp[5] = 0; arg_tmp[6] = 0; arg_tmp[7] = 0;
		arg_tmp[8] = 0; arg_tmp[9] = 0; arg_tmp[10]= 0; arg_tmp[11]= 0;
		arg_tmp[12]= 0; arg_tmp[13]= 0; arg_tmp[14]= 0; arg_tmp[15]= 0;
	}
	void ShaderField::setBase()
	{
	}
	void ShaderField::setFunction(const char* source, ShaderInterface::Version reparse)
	{
		std::string tmp(source);
		setFunction(tmp);
	}
	void ShaderField::setFunction(const std::string &source, ShaderInterface::Version reparse)
	{
		switch(reparse) {
		case ShaderInterface::AUTO_DETECT:
		case ShaderInterface::GLSL1_1:
			ShaderInterface::reparseGLSL1_1(source, orig_source, orig_args);
			break;
		default:
			orig_source = source;
			break;
		}
	}
	void ShaderField::argf(
		double  a0, double  a1, double  a2, double  a3,
		double  a4, double  a5, double  a6, double  a7,
		double  a8, double  a9, double a10, double a11,
		double a12, double a13, double a14, double a15
	)
	{
		arg_tmp[0] =  a0; arg_tmp[1] =  a1; arg_tmp[2] =  a2; arg_tmp[3] =  a3;
		arg_tmp[4] =  a4; arg_tmp[5] =  a5; arg_tmp[6] =  a6; arg_tmp[7] =  a7;
		arg_tmp[8] =  a8; arg_tmp[9] =  a9; arg_tmp[10]= a10; arg_tmp[11]= a11;
		arg_tmp[12]= a12; arg_tmp[13]= a13; arg_tmp[14]= a14; arg_tmp[15]= a15;
	}
	void ShaderField::cache(DrawableWithCache &target)
	{
		api.cacheField(orig_source, orig_args, target);
	}
	void ShaderField::cache(const char *source, DrawableWithCache &target)
	{
		if(!initialized && &target!=0) {
			setFunction(source);
			cache(target);
			initialized = true;
		}
	}
	void ShaderField::cacheFromFile(const std::string path, DrawableWithCache &target)
	{
		std::stringstream buf;
		Internal::loadGLSL(buf, path);
		Internal::error_message.clear();
		try{
			setFunction(buf.str().c_str());
			cache(*Display::the_canvas);
		} catch (Exception e) {
			Internal::error_message = e.getErrorString();
			Internal::logError();
		} catch (Exception *e) {
			Internal::error_message = e->getErrorString();
			Internal::logError();
		}
	}
	void ShaderField::draw(const Rectangle &rect, const double* argv, const int argn, Drawable &target)
	{
		api.drawField(rect, argv, argn);
	}
	void ShaderField::to(Image &dest, const Rectangle &rect, const double* argv, const int argn, Canvas &media)
	{
		api.fieldToImage(dest, rect, argv, argn, media);
	}
	void ShaderField::draw(const Rectangle &rect, Drawable &target)
	{
		api.drawField(rect, arg_tmp, 4);
	}
	void ShaderField::to(Image &dest, const Rectangle &rect, Canvas &media)
	{
		api.fieldToImage(dest, rect, arg_tmp, 4, media);
	}



	ShaderCoordinateTuner::ShaderCoordinateTuner()
	{
		cache();
		set(256*pixel, 256*pixel);
	}
	ShaderCoordinateTuner& ShaderCoordinateTuner::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderCoordinateTuner& ShaderCoordinateTuner::draw(Canvas &target)
	{
		cache(target);
		const double Z[4] = { 0,0,0,0 };
		field.draw(*this, Z, 0, target);
		return *this;
	}
	void ShaderCoordinateTuner::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[4] = { 0,0,0,0 };
		field.to(dest, *this, Z, 0, media);
	}
	ShaderField ShaderCoordinateTuner::field;
	const char *ShaderCoordinateTuner::glsl_source =
			"void main(void){ "
			"float r = floor(abs(gl_TexCoord[0][0]))/255.0;"
			"float g = fract(abs(gl_TexCoord[0][0]));"
			"pix( r, g, 0.0); }";



	ShaderGrating::ShaderGrating()
	{
		cache();
		set(10*pixel, 10*pixel);
	}
	ShaderGrating& ShaderGrating::setWave(double wavelen, double cont, double orient, double phs)
	{
		return setWave(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderGrating& ShaderGrating::setWave(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast = cont;
		wavelength = wavelen;
		orientation = orient;
		phase = phs;
		return *this;
	}
	ShaderGrating& ShaderGrating::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderGrating& ShaderGrating::draw(Drawable &target)
	{
		cache();
		//const double Z[4] = { phase.at_degree(), contrast, orientation.at_degree(), PI/wavelength*getWidth() };
		const double Z[4] = { phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength };
		field.draw(*this, Z, 1, target);
		return *this;
	}

	void ShaderGrating::to(Image &dest, Canvas &media)
	{
		cache(media);
//		const double Z[4] = { phase.at_degree(), contrast, orientation.at_degree(), PI/wavelength*getWidth() };
		const double Z[4] = { phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength };
		field.to(dest, *this, Z, 1, media);
	}

	ShaderField ShaderGrating::field;
	const char *ShaderGrating::glsl_source =
			"void main(void){ "
			"float phase = gl_TexCoord[4][0], contrast = gl_TexCoord[4][1], orientation = gl_TexCoord[4][2], frequency = gl_TexCoord[4][3];"
			"float _x = sin(orientation)*xp()-cos(orientation)*yp();"
			"pix(0.5 + contrast*0.5*cos(frequency*_x + phase) ); }";


	ShaderGaussianDot::ShaderGaussianDot()
	{
		cache();
		bgcolor = Color::null_color;
		Rectangle::fill = Color::black;
	}
	ShaderGaussianDot& ShaderGaussianDot::setSigma(double sigma)
	{
		Rectangle::set(sigma*8.0, sigma*8.0);
		return *this;
	}
	ShaderGaussianDot& ShaderGaussianDot::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderGaussianDot& ShaderGaussianDot::draw(Drawable &target)
	{
		cache();
		const double Z[12] = {
			2.0/getWidth(), 0, 0, 0,
			Rectangle::fill.getR(), Rectangle::fill.getG(), Rectangle::fill.getB(), Rectangle::fill.getA(),
			bgcolor.getR(), bgcolor.getG(), bgcolor.getB(), bgcolor.getA()
		};
		field.draw(*this, Z, 3, target);
		return *this;
	}
	void ShaderGaussianDot::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[12] = {
			2.0/getWidth(), 0, 0, 0,
			Rectangle::fill.getR(), Rectangle::fill.getG(), Rectangle::fill.getB(), Rectangle::fill.getA(),
			bgcolor.getR(), bgcolor.getG(), bgcolor.getB(), bgcolor.getA()
		};
		field.to(dest, *this, Z, 3, media);
	}
	ShaderField ShaderGaussianDot::field;
	const char *ShaderGaussianDot::glsl_source =
			"void main(void) {"
			"float r = rp()*gl_TexCoord[4][0]*4.0;"
			"float r2 = -(r*r) / 2.0;"
			"vec4 env = mix(gl_TexCoord[6], gl_TexCoord[5], exp(r2));"
			"pix(env);"
			"}";


	ShaderGabor::ShaderGabor()
	{
		cache();
		set(10*pixel, 10*pixel);
		contrast = 1;
		wavelength = 5;
		orientation = 0;
		phase = 0;
	}
	ShaderGabor& ShaderGabor::setSigma(double sigma)
	{
		return setSigma(sigma*pixel);
	}
	ShaderGabor& ShaderGabor::setSigma(Length sigma)
	{
		Rectangle::set(sigma*8.0, sigma*8.0);
		return *this;
	}
	ShaderGabor& ShaderGabor::setWave(double wavelen, double cont, double orient, double phs)
	{
		return setWave(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderGabor& ShaderGabor::setWave(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast = cont;
		wavelength = wavelen;
		orientation = orient;
		phase = phs;
		return *this;
	}

	ShaderGabor& ShaderGabor::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderGabor& ShaderGabor::draw(Drawable &target)
	{
		cache();
		const double Z[8] = { 2.0/getWidth(),0,0,0, phase, contrast, orientation, 2*PI/wavelength };
		field.draw(*this, Z, 2, target);
		return *this;
	}
	void ShaderGabor::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[8] = { 2.0/getWidth(),0,0,0, phase, contrast, orientation, 2*PI/wavelength };
		field.to(dest, *this, Z, 2, media);
	}

	ShaderField ShaderGabor::field;
	const char *ShaderGabor::glsl_source =
			"void main(void) {"
			"float _r = rp()*gl_TexCoord[4][0]*4.0;"
			"float env = exp( -(_r*_r) / 2.0 );"
			"float phase = gl_TexCoord[5][0], contrast = gl_TexCoord[5][1], orientation = gl_TexCoord[5][2], frequency = gl_TexCoord[5][3];"
			"float _x = sin(orientation)*xp()-cos(orientation)*yp();"
			//"float level = 0.5+contrast*0.5*cos(frequency*_x + phase);"
			//"pix(level,env);"
			"float level = 0.996078*(0.5+env*( contrast*0.5*cos(frequency*_x + phase) ));"
			"pix(level);"
			"}";



	ShaderGaborAlpha::ShaderGaborAlpha()
	{
		cache();
		set(10*pixel, 10*pixel);
		contrast = 1;
		wavelength = 5;
		orientation = 0;
		alpha = 1.0;
		phase = 0;
	}
	ShaderGaborAlpha& ShaderGaborAlpha::setSigma(double sigma)
	{
		return setSigma(sigma*pixel);
	}
	ShaderGaborAlpha& ShaderGaborAlpha::setSigma(Length sigma)
	{
		Rectangle::set(sigma*8.0, sigma*8.0);
		return *this;
	}
	ShaderGaborAlpha& ShaderGaborAlpha::setWave(double wavelen, double cont, double orient, double phs)
	{
		return setWave(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderGaborAlpha& ShaderGaborAlpha::setWave(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast = cont;
		wavelength = wavelen;
		orientation = orient;
		phase = phs;
		return *this;
	}

	ShaderGaborAlpha& ShaderGaborAlpha::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderGaborAlpha& ShaderGaborAlpha::draw(Drawable &target)
	{
		cache();
		const double Z[8] = { 2.0/getWidth(),alpha,0,0, phase, contrast, orientation, 2*PI/wavelength };
		field.draw(*this, Z, 2, target);
		return *this;
	}
	void ShaderGaborAlpha::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[8] = { 2.0/getWidth(),alpha,0,0, phase, contrast, orientation, 2*PI/wavelength };
		field.to(dest, *this, Z, 2, media);
	}

	ShaderField ShaderGaborAlpha::field;
	const char *ShaderGaborAlpha::glsl_source =
			"void main(void) {"
			"float _r = rp()*gl_TexCoord[4][0]*4.0;"
			"float env = exp( -(_r*_r) / 2.0 ) * gl_TexCoord[4][1];"
			"float phase = gl_TexCoord[5][0], contrast = gl_TexCoord[5][1], orientation = gl_TexCoord[5][2], frequency = gl_TexCoord[5][3];"
			"float _x = sin(orientation)*xp()-cos(orientation)*yp();"
			"float level = 0.5+contrast*0.5*cos(frequency*_x + phase);"
			"pix(level,env);"
			"}";




	ShaderPlaid::ShaderPlaid()
	{
		cache();
		set(10*pixel, 10*pixel);
	}
	ShaderPlaid& ShaderPlaid::setSigma(double sigma)
	{
		return setSigma(sigma*pixel);
	}
	ShaderPlaid& ShaderPlaid::setSigma(Length sigma)
	{
		Rectangle::set(sigma*8.0, sigma*8.0);
		return *this;
	}
	ShaderPlaid& ShaderPlaid::setWave(double wavelen, double cont, double orient, double phs)
	{
		return setWave(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderPlaid& ShaderPlaid::setWave(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast = cont;
		wavelength = wavelen;
		orientation = orient;
		phase = phs;
		return *this;
	}
	ShaderPlaid& ShaderPlaid::setWave2(double wavelen, double cont, double orient, double phs)
	{
		return setWave2(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderPlaid& ShaderPlaid::setWave2(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast2 = cont;
		wavelength2 = wavelen;
		orientation2 = orient;
		phase2 = phs;
		return *this;
	}
	ShaderPlaid& ShaderPlaid::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderPlaid& ShaderPlaid::draw(Drawable &target)
	{
		cache();
		const double Z[12] =
		{
			2.0/getWidth(),0,0,0,
			phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength,
			phase2.at_degree(), contrast2, orientation2.at_degree(), 2*PI/wavelength2
		};
		field.draw(*this, Z, 3, target);
		return *this;
	}

	void ShaderPlaid::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[12] =
		{
			2.0/getWidth(),0,0,0,
			phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength,
			phase2.at_degree(), contrast2, orientation2.at_degree(), 2*PI/wavelength2
		};
		field.to(dest, *this, Z, 3, media);
	}

	ShaderField ShaderPlaid::field;
	const char *ShaderPlaid::glsl_source =
			"void main(void) {"
			"float phase = gl_TexCoord[5][0], contrast = gl_TexCoord[5][1], orientation = gl_TexCoord[5][2], frequency = gl_TexCoord[5][3];"
			"float phase2 = gl_TexCoord[6][0], contrast2 = gl_TexCoord[6][1], orientation2 = gl_TexCoord[6][2], frequency2 = gl_TexCoord[6][3];"
			"float _x  = sin(orientation)*xp()-cos(orientation)*yp();"
			"float _x2 = sin(orientation2)*xp()-cos(orientation2)*yp();"
			"float _r = rp()*gl_TexCoord[4][0]*4.0;"
			"float env = exp( -(_r*_r) / 2.0 );"
			"float level = 0.996078*(0.5+env*( contrast*0.5*cos(frequency*_x + phase) + contrast2*0.5*cos(frequency2*_x2 + phase2) ));"
			"pix(level);"
			//"float level = 0.5 + contrast*0.5*cos(frequency*_x + phase) + contrast2*0.5*cos(frequency2*_x2 + phase2);"
			//"pix(level, level, level, env);"
			"}";



	ShaderPlaidAlpha::ShaderPlaidAlpha()
	{
		cache();
		set(10*pixel, 10*pixel);
		alpha = 1.0;
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setSigma(double sigma)
	{
		return setSigma(sigma*pixel);
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setSigma(Length sigma)
	{
		Rectangle::set(sigma*8.0, sigma*8.0);
		return *this;
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setWave(double wavelen, double cont, double orient, double phs)
	{
		return setWave(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setWave(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast = cont;
		wavelength = wavelen;
		orientation = orient;
		phase = phs;
		return *this;
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setWave2(double wavelen, double cont, double orient, double phs)
	{
		return setWave2(wavelen*pixel, cont, orient*degree, phs*degree);
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::setWave2(Length wavelen, double cont, Angle orient, Angle phs)
	{
		contrast2 = cont;
		wavelength2 = wavelen;
		orientation2 = orient;
		phase2 = phs;
		return *this;
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::cache(DrawableWithCache &target)
	{
		field.cache(glsl_source, target);
		return *this;
	}
	ShaderPlaidAlpha& ShaderPlaidAlpha::draw(Drawable &target)
	{
		cache();
		const double Z[12] =
		{
			2.0/getWidth(),alpha,0,0,
			phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength,
			phase2.at_degree(), contrast2, orientation2.at_degree(), 2*PI/wavelength2
		};
		field.draw(*this, Z, 3, target);
		return *this;
	}

	void ShaderPlaidAlpha::to(Image &dest, Canvas &media)
	{
		cache(media);
		const double Z[12] =
		{
			2.0/getWidth(),alpha,0,0,
			phase.at_degree(), contrast, orientation.at_degree(), 2*PI/wavelength,
			phase2.at_degree(), contrast2, orientation2.at_degree(), 2*PI/wavelength2
		};
		field.to(dest, *this, Z, 3, media);
	}

	ShaderField ShaderPlaidAlpha::field;
	const char *ShaderPlaidAlpha::glsl_source =
			"void main(void) {"
			"float phase = gl_TexCoord[5][0], contrast = gl_TexCoord[5][1], orientation = gl_TexCoord[5][2], frequency = gl_TexCoord[5][3];"
			"float phase2 = gl_TexCoord[6][0], contrast2 = gl_TexCoord[6][1], orientation2 = gl_TexCoord[6][2], frequency2 = gl_TexCoord[6][3];"
			"float _x  = sin(orientation)*xp()-cos(orientation)*yp();"
			"float _x2 = sin(orientation2)*xp()-cos(orientation2)*yp();"
			"float _r = rp()*gl_TexCoord[4][0]*4.0;"
			"float env = exp( -(_r*_r) / 2.0 ) * gl_TexCoord[4][1];"
			//"float level = 0.996078*(0.5+env*( contrast*0.5*cos(frequency*_x + phase) + contrast2*0.5*cos(frequency2*_x2 + phase2) ));"
			//"pix(level);"
			"float level = 0.5 + contrast*0.5*cos(frequency*_x + phase) + contrast2*0.5*cos(frequency2*_x2 + phase2);"
			"pix(level, level, level, env);"
			"}";


}	/*	<- namespace Figures 	*/
}	/*	<- namespace Psycholops 	*/

