/** 
Modulated Audification of the Ising model in 2d

-> reads the model on a Hilbert curve (needs classes HilbertIndices.sc and Pturtle.sc)
-> generates sound wave out of spin states

-> gives GUI to show the model and a controll GUI for the model (starting configuration and coupling) and starts/ends the audification
**/

(
q.clear;
p.clear;

q = q ? ();
p = p ?? { ProxySpace(s, \p) }; p.push;
s.boot;
)

// ISING-model in 2d, d=64x64 and its GUI-window;
(
			// constants
q.dimensions = 64;
q.coupling = 0.42;     // equivalent to Tcrit: j = 0.42 (if magn.=0), (range i.e. 0.3 - 0.5)
q.magnetization = 0.0;
q.timeStep = 0.5;

			// data frame
q.data;

			// functions
q.randData = {|e|
	e.data = Array.fill2D(q.dimensions, q.dimensions, { #[-1, 1].choose });
	e.data;
};

q.nullData = { q.data = Array.fill2D(q.dimensions, q.dimensions,  0); q.data; };

q.neighSum = {|e, y, x|		// sum of neighbours
	var data = e.data;
	var spinSum = 
		data.wrapAt(y).wrapAt(x + 1) + 
		data.wrapAt(y).wrapAt(x - 1) +
		data.wrapAt(y - 1).wrapAt(x) +
		data.wrapAt(y + 1).wrapAt(x);
	spinSum;
};

q.nextFrame = { |e|
	q.data.do { |line, lineIndex|
		q.data.at(lineIndex).do { |spin, colIndex|
			q.data[lineIndex][colIndex] = q.formula(q.neighSum(lineIndex, colIndex), spin);
		}
	}
};

q.nextStep = {|e| e.data = e.nextFrame; e.data };

		// GUI
q.makeWin = {	 
	var size = 5;
	w = SCWindow("Ising(Test)", Rect(30, 240, 360, 360)).front;
	w.view.background_(Color.grey);
	
	w.drawHook = { 
		var black, white;
		black = Color.black; white = Color.white; 
		q.data.do { |line, i| 
			line.do { |val, j| 
						// set the Color depending on value
				[black, white].clipAt(val).set;
				Pen.addArc( (i * size + 10)@(j* size + 10), 1.5, 0, 2pi);
				Pen.fill;
			}
		}
	};
	w.refresh;
};

q.formula = {|e, spinSum, spin| // number of neighbours
	var energy, random; 
	energy = -2 * ((q.coupling * spinSum) + q.magnetization) * spin; 
	random = rrand (0.0, 1.0); 
	if (energy >= 0, { 	// most neighbours are different -> flip spin
		spin * -1
	}, { 				// else: if spinSum near 0, spin random, depend. on temp (=inverse coupling);
						
		if(random <= exp(energy), {spin * -1;}, {spin}); 
	}); 
};
q.makeWin;
q.dimensions=100;
q.randData; //q.data.printAll; 
w.refresh; "";

Tdef(\model, { 
	inf.do { |i| 
		q.nextStep;
		if (i % 10 == 0) { { w.refresh; }.defer }; //every 10th sweep shown!
		0.15.wait;
	}
});
Tdef(\model).stop;
)

// MODULATED AUDIFICATION: Ising-2d read on a Hilbert curve
(
q.hilb64 = HilbertIndex(6).all;

q.buf1 = Buffer.alloc(s, 4096, 1);

q.loadData2Buf = { 
	q.buf1.loadCollection(	
		q.hilb64.collect({ |xyIndex| 	
			var x, y; #x, y = xyIndex;
			q.data[x][y]
		})
	);
};

q.loadData2Buf; 

~playBuf = { |sustain=0.2| 
	SinOsc.ar(
	200 * PlayBuf.ar(1, q.buf1.bufnum, q.buf1.numFrames / s.sampleRate / sustain, loop: 1) + 400
	) * 0.2 ! 2
};
 	// testing the sounds
Tdef(\audif, { 
	~playBuf.play;
	inf.do { 
		q.loadData2Buf; 
		0.2.wait; 
	}
});
)

//////////////////////////////////////////////////////////////////////////////////////////

// MAKING THE CONTROL GUI:
(
var v, slid1, b1, b2, b3, b4, b5, b6, b7;

	v=SCWindow("Ising model (2D) and audification", Rect(100,500,350,180)); 
	v.view.decorator = FlowLayout(v.view.bounds);

	slid1 = EZSlider(v, 300@30, "Coupling", 
		ControlSpec(0.1, 0.9, \lin, 0.01), 
		{|ez| q.coupling=ez.value; r.coupling=ez.value; }
	);
	v.view.decorator.nextLine;

// start and stop model in 2d:
	b1 = SCButton(v,Rect(10,10,100,50));
	b1.states = [
				["model-2D: run",Color.black, Color.new255(212, 212, 212)],
				["model-2D: stop",Color.black, Color.new255(124, 252, 0)]
			];
	b1.action = { arg butt;
		if(butt.value==1, { 
			Tdef(\model).play;
			//w.front;
			}, {
			Tdef(\model).stop;
		})	
		};

// start and stop audification:
	b2 = SCButton(v,Rect(120,10,100,50));
	b2.states = [
				["audification: run",Color.black, Color.new255(212, 212, 212)],
				["audification: stop",Color.black, Color.new255(124, 252, 0)]
			];
	b2.action = { arg butt;
		if(butt.value==1, { 
			Tdef(\audif).play;
			//w.front;
			}, {
			Tdef(\audif).stop;
			~playBuf.stop;
		})	
		};

	v.view.decorator.nextLine;
	b3 = SCButton(v,Rect(120,10,80,30));
	b3.states = [
		["randomize", Color.black, Color.red]
	];
	b3.action = { 
		q.randData;w.refresh;//w.front;
	};

	b4 = SCButton(v,Rect(120,10,60,30));
	b4.states = [
		["all up", Color.black, Color.red]
	];
	b4.action = { 
		q.data = 1 ! 64 ! 64;w.refresh;//w.front;
	};

	b5 = SCButton(v,Rect(120,10,60,30));
	b5.states = [
		["all down", Color.black, Color.red]
	];
	b5.action = { 
		q.data = -1 ! 64 ! 64;w.refresh;//w.front;
	};

	v.view.decorator.nextLine;
v.front;
)