Grid a simple GUI grid


This GUI widget can be useful in various situations such as representing scales,

step sequencers, etc. In some cases it might be better to use the class BoxGrid.


The grid is basically a graphical representation of 2 dimensional array of

columns and rows. 


For example, here we have 2 columns with 3 rows: [[0, 1, 0], [1, 1 0]]

(1 and 0 indicating the state of the node in that location)


The getState and setState_ methods function according to the convention of 

passing (x, y), i.e. the horizontal first and then the vertical.


*new(window, bounds, columns, rows, border);

window - A SCWindow in which the Grid will appear

bounds - The Rect of the Grid 

columns - The number of columns in the grid (vertical lines)

rows - The number of rows in the grid (horizontal lines)

border - Boolean. Border around the grid.

nodeDownAction_ - The function that mouse down on a node will trigger.

nodeTrackAction_ - The function that dragging the cursor will trigger.

nodeUpAction_ - The function that mouse up on a node will trigger.


fillGrid - turns all the nodes on.

clearGrid - turns all the nodes off.

setNodeShape_(string) - The shape of the node ("circle" or "square").

setBorder_(boolean) - border around the Grid.

setFillMode_(boolean) - Whether the nodes are filled with color or not.

setFillColor_(color) - The color of the filled nodes.

setTrailDrag_(bool, bool) - Mouse tracking on the grid leaves a trail of on states (or switches).

- The first argument is the trails, the second is if it switches on/off.

setNodeStates_(array) - Updates the grid from a 2 dimensional array of columns and rows.

getNodeStates - Returns 2 dimensional array of columns and rows.

setNodeSize_(int) - The size of the nodes

setState_(row, column, state) - Sets the state of an individual node.

getState(row, column) - Returns the state of an individual node.

setNodeColor_(row, col, color) - Sets the color of an individual box.

getNodeColor(row, column) - Returns the color of an individual box.

setBackgrColor_(Color) - Sets the background color of the grid.

setBackgrDrawFunc_(func) - Allows Pen to draw behind the grid.

remove - Remove the view.



Note: the only methods that differ between BoxGrid and Grid are : setNodeBorder_/setNodeSize_

respectively, and the BoxGrid doesn't take the border argument. There is no "nodeshape" in BoxGrid.

Apart from these differences, the code should run the same, so it's easy to change between the two.



// choose a gui (see GUI helpfile)

GUI.swing;

GUI.cocoa;



Grid.new


Grid.new(bounds: Rect(0, 0, 600, 400), columns: 4, rows: 5, border:false)


Grid.new(bounds: Rect(20, 20, 600, 20), columns: 24, rows: 1, border:false)


Grid.new( bounds: Rect(20, 20, 460, 140), columns: 24, rows: 4, border:true);


(


w = Window.new("Testing Grid", Rect(10, 500, 500, 212)).front;

Grid.new(w, bounds: Rect(20, 20, 460, 20), columns: 24, rows: 1, border:false);

Grid.new(w, bounds: Rect(20, 60, 460, 20), columns: 24, rows: 1, border:false);

Grid.new(w, bounds: Rect(20, 100, 460, 20), columns: 24, rows: 1, border:false);

Grid.new(w, bounds: Rect(20, 140, 460, 20), columns: 24, rows: 1, border:false);


)



(


w = Window.new("Testing Grid", Rect(10, 500, 500, 212)).front;

a = Grid.new(w, bounds: Rect(20, 20, 460, 140), columns: 24, rows: 4, border:false);


)


a.setBackgrColor_(Color.white);

a.setBorder_(true)


// you can use either boolean

a.setState_(1,1,true)

// or integer (1 = true, 0 = false)

a.setState_(2,3,1)


// get state will return only integers

a.getState(1,3)

a.getState(2,3)


// get the states of all nodes

b = a.getNodeStates

// or just the first row:

c = a.getNodeStates[0]


// clear the grid

a.clearGrid


// set it back to previous state

a.setNodeStates_(b)


// the shape of the nodes

a.setNodeShape_("circle");

a.setNodeShape_("square");


a.setNodeSize_(8);

a.setNodeSize_(12);


// fillmode and color

a.setFillMode_(true);

a.setFillColor_(Color.blue);

a.setFillColor_(Color.white);


a.setNodeColor_(1, 1, Color.red);

a.setNodeColor_(0, 0, Color.green);


a.setFillMode_(false);

a.setBackgrColor_(Color.clear);

a.setBackgrColor_(Color.white);


a.setTrailDrag_(true); // leaves a trail of active nodes when moved around the grid

a.setTrailDrag_(true, true); // bool true (so it switches the state of boxes that you move over

a.setTrailDrag_(true, false); // bool false

a.setTrailDrag_(false, false); // back to default



// or

a.gridNodes[0][3].setState_(true)

a.refresh






(


w = Window.new("Testing Grid", Rect(100, 500, 500, 212)).front;

a = Grid.new(w, bounds: Rect(20, 20, 460, 140), columns: 24, rows: 8, border:true)

.setBackgrColor_(Color.white)

.setBorder_(true)

.setNodeShape_("circle")

.setNodeSize_(8)

.setFillMode_(true)

.setFillColor_(Color.new255(103, 148, 103))

.nodeDownAction_({arg nodeloc; nodeloc.postln;})

.nodeTrackAction_({arg nodeloc; nodeloc.postln;})

.nodeUpAction_({arg nodeloc; nodeloc.postln}); 


30.do({

a.setState_(24.rand, 8.rand, 1); // column, row, state

});


)



b = a.getNodeStates

// clear the grid

a.clearGrid

// set it back to previous state

a.setNodeStates_(b)



// draw on the same GUI as the grid (labelling the nodes for example)

// see also the example with setBackgrDrawFunc_ here below

(

w = Window.new("Testing Grid", Rect(10, 500, 500, 212)).front;

14.do({arg i;

SCStaticText(w, Rect(10+((i+1)*30), 30, 100, 20))

.string = i.asString;

});

a = Grid.new(w, bounds: Rect(20, 20, 460, 140), columns: 14, rows: 4, border:false);

//a.setBackgrColor_(Color.white);

)






/////////////////// some examples from the development of Grid


// the synthdef used in the examples

(

SynthDef(\pure, {arg freq=440, pan=0.0, vol=0.3, envdur=0.8;

var signal, envArray, env;

env = EnvGen.kr(Env.perc(0.01, envdur), doneAction:2); signal = Pan2.ar(SinOsc.ar(freq, 0, vol), pan) * env;

Out.ar(0, signal);

}).add;

)


///////////// scales

// here we have one octave of 19 equal tempered scale (19-TET)


(

var clock, scale;

var nTET = 19; // change this variable to something else (5, 12, 24, etc.)


w = Window.new("Testing Grid", Rect(10, 500, 800, 100+ (20*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET) * 440;}).reverse; // 19-TET scale in A


a = Grid.new(w, bounds: Rect(20, 20, 760, 20), columns: 32, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.white);

b = Grid.new(w, bounds: Rect(20, 50, 760, 20*nTET), columns: 32, rows: nTET, border:true)

.setBackgrColor_(Color.white)

.setBorder_(true)

.setNodeShape_("circle")

.setNodeSize_(8)

.setFillMode_(true)

.setFillColor_(Color.new255(103, 148, 103))

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, k[nodeloc[1]], \envType, 1])})

.nodeUpAction_({arg nodeloc; nodeloc.postln; b.setState_(nodeloc[0], nodeloc[1], 0)}); 


// fill it with random nodes

(nTET*3).do({

b.setState_(32.rand, nTET.rand, 1); // column, row, state

});


)



// and we can play the grid from above with a clock

(

var nTET = 19;

t = TempoClock(4);

t.schedAbs(t.beats.ceil, { arg beat, sec;


{a.setState_( beat%32, 0, 1)}.defer;

{a.setState_( beat%32, 0, 0)}.defer(t.beatDur);


nTET.do({arg i;

if(b.getState( beat%32, i) == 1, {

Synth(\pure, [\freq, k[i], \envType, 1]);

})

});

1;

});

b.nodeUpAction_({nil;}); // remove the mouseUp function (so the nodes stay)


)


b.clearGrid



// ----------------------------------------------------------------------


//  the same as above but with some differences:

//  horizontal location is the pitch in one octave

//  vertical location is octave higher/lower



(

var clock, scale;

var nTET = 19; // change this variable to something else (5, 12, 24, etc.)

var octaves = 8;


w = Window.new("Testing Grid", Rect(10, 500, 800, 100+ (20*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET);}); // 19-TET scale in A

n = Array.fill(octaves, {arg i; k * (2.pow(i)*110)}).reverse;


a = Grid.new(w, bounds: Rect(20, 20, 760, 20), columns: nTET, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.white);

b = Grid.new(w, bounds: Rect(20, 50, 760, 20*nTET), columns: nTET, rows: octaves, border:true)

.setBackgrColor_(Color.new255(203, 248, 203))

.setBorder_(true)

.setNodeShape_("square")

.setNodeSize_(12)

.setFillMode_(true)

.setFillColor_(Color.new255(103, 148, 103))

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])})

.nodeUpAction_({arg nodeloc; nodeloc.postln; b.setState_(nodeloc[0], nodeloc[1], 0)}); 


)


// and we can play the grid from above with a clock

(

var nTET = 19;

var octaves = 8;


t = TempoClock(4);

t.schedAbs(t.beats.ceil, { arg beat, sec;


{a.setState_(beat%nTET, 0, 1)}.defer;

{a.setState_(beat%nTET, 0, 0)}.defer(t.beatDur);


octaves.do({arg i;

if(b.getState(beat%nTET, i) == 1, {

Synth(\pure, [\freq, n[i][beat%nTET], \envType, 1]);

})

});

1;

});

b.nodeUpAction_({nil;}); // remove the mouseUp function (so the nodes stay)


)


t.tempo_(6)


// -------------------------------------------



// I'm not sure what this is... but it's fun!

// note the pitch algorithm in the array n is different from the above (where we have octaves)

(

var clock, scale;

var nTET = 19; // change this variable to something else (5, 12, 24, etc.)

var octaves = 8;


z = [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 ], [ 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 ], [ 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ];


w = Window.new("Testing Grid", Rect(10, 500, 800, 100+ (20*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET);}); // 19-TET scale in A

n = Array.fill(octaves, {arg i; k * (1.5.pow(i)*110)}).reverse;


a = Grid.new(w, bounds: Rect(20, 20, 760, 20), columns: nTET, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.white);

b = Grid.new(w, bounds: Rect(20, 50, 760, 20*nTET), columns: nTET, rows: octaves, border:true)

.setBackgrColor_(Color.new255(203, 248, 203))

.setBorder_(true)

.setNodeShape_("square")

.setNodeSize_(12)

.setFillMode_(true)

.setNodeStates_(z)

.setFillColor_(Color.new255(103, 148, 103))

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])});


t = TempoClock(6);

t.schedAbs(t.beats.ceil, { arg beat, sec;


{a.setState_(beat%nTET, 0, 1)}.defer;

{a.setState_(beat%nTET, 0, 0)}.defer(t.beatDur);


octaves.do({arg i;

if(b.getState(beat%nTET, i) == 1, {

Synth(\pure, [\freq, n[i][beat%nTET], \envType, 1]);

})

});

1;

});

)



//////



// same as above but without a TempoClock and here you can use the

// top grid to scroll through the chords, like strumming strings or.... err...

(

var clock, scale;

var nTET = 19; // change this variable to something else (5, 12, 24, etc.)

var octaves = 8;


z = [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 ], [ 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 ], [ 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ];


w = Window.new("Testing Grid", Rect(10, 500, 420, 100+ (10*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET);}); // 19-TET scale in A

n = Array.fill(octaves, {arg i; k * (1.5.pow(i)*110)}).reverse;


a = Grid.new(w, bounds: Rect(20, 20, 380, 20), columns: nTET, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.white)

.nodeDownAction_({arg nodeloc; 

octaves.do({arg i;

if(b.getState(nodeloc[0], i) == 1, {

Synth(\pure, [\freq, n[i][nodeloc[0]], \envType, 1]);

})

});

})

.nodeUpAction_({arg nodeloc; 

a.setState_(nodeloc[0], nodeloc[1], 0)

})

.nodeTrackAction_({arg nodeloc; 

octaves.do({arg i;

if(b.getState(nodeloc[0], i) == 1, {

Synth(\pure, [\freq, n[i][nodeloc[0]], \envType, 1]);

})

});

});


b = Grid.new(w, bounds: Rect(20, 50, 380, 10*nTET), columns: nTET, rows: octaves, border:true)

.setBackgrColor_(Color.new255(203, 248, 203))

.setBorder_(true)

.setNodeShape_("square")

.setNodeSize_(8)

.setFillMode_(true)

.setNodeStates_(z)

.setFillColor_(Color.new255(103, 148, 103))

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])});

)




//----------------------------------------------------



///////// a step sequencer

(

var gridArray, clockArray, meter, freq;


w = Window.new("Step sequencer", Rect(10, 500, 400, 250)).front;


a = Grid.new(w, bounds: Rect(20, 20, 360, 20), columns: 16, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.gray);


gridArray = Array.fill(4, {arg i;

Grid.new(w, bounds: Rect(20, 80+(i*30), 360, 20), columns: 16, rows: 1)

.setFillMode_(true)

.setFillColor_(Color.white);

});


t = TempoClock(4);

t.schedAbs(t.beats.ceil, { arg beat, sec;


{a.setState_( beat%16, 0, 1)}.defer;

{a.setState_( beat%16, 0, 0)}.defer(t.beatDur);

4.do({arg i;

if(gridArray[i].getState(beat%16, 0) == 1, {

Synth(\pure, [\freq, 440*(i+1), \envType, 1]);

});

});

1;

});

)


t.tempo_(12)






//////////////// polyrhythm


(

var gridArray, clockArray, meter, freq;

meter = [4,3,5];


w = Window.new("polyrhythm", Rect(10, 500, 400, 150)).front;


gridArray = Array.fill(3, {arg i;

Grid.new(w, bounds: Rect(20, 20+(i*30), 360, 20), columns: meter[i], rows: 1)

.setFillMode_(true)

.setFillColor_(Color.white);

});


clockArray = Array.fill(3, {arg i;

t = TempoClock(4);

t.schedAbs(t.beats.ceil, { arg beat, sec;

if(gridArray[i].getState(beat%meter[i], 0) == 1, {

Synth(\pure, [\freq, 440*(i+1), \envType, 1]);

});

1;

});

});

)







// in case you want to draw behind the Grid then use the setBackgrDrawFunc

// here is a typical keyboard for 12-TET. How would you draw a keyboard for a 19-TET tuning?


(

var clock, scale;

var nTET = 12;

var octaves = 8;

w = Window.new("Keyboard", Rect(10, 500, 800, 100+ (20*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET);});

n = Array.fill(octaves, {arg i; k * (2.pow(i)*110)}).reverse;


b = Grid.new(w, bounds: Rect(20, 50, 760, 20*nTET), columns: nTET, rows: 8, border:true)

.setBorder_(true)

.setBackgrColor_(Color.new255(103, 148, 103))

.setNodeShape_("square")

.setNodeSize_(10)

.setFillMode_(true)

.setFillColor_(Color.white)

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])})

.setBackgrDrawFunc_({

12.do({arg i;

if((i==1)||(i==3)||(i==6)||(i==8)||(i==10),{

Color.new255(60, 60, 60).set;

},{

Color.new255(255, 255, 255).set;

});

Pen.fillRect(Rect(34+(i*(700/12)), 0, 600/12, 240));

})

});

)




// - eh? 

(

var clock, scale;

var nTET = 19;

var octaves = 8;

w = Window.new("Keyboard", Rect(10, 500, 800, 100+ (20*nTET))).front;


k = Array.fill(nTET, {arg i; 2.pow(i/nTET);}); // 19-TET scale in A

n = Array.fill(octaves, {arg i; k * (2.pow(i)*110)}).reverse;


a = Grid.new(w, bounds: Rect(20, 20, 760, 20), columns: nTET, rows: 1, border: false)

.setFillMode_(true)

.setFillColor_(Color.white)

.nodeTrackAction_({arg nodeloc; 

octaves.do({arg i;

if(b.getState(i, nodeloc[1]) == 1, {

Synth(\pure, [\freq, n[i][nodeloc[1]]]);

})

});

});


b = Grid.new(w, bounds: Rect(20, 50, 760, 20*nTET), columns: nTET, rows: 8, border:true)

.setBorder_(true)

.setBackgrColor_(Color.new255(133, 178, 133))

.setNodeShape_("square")

.setNodeSize_(8)

.setFillMode_(true)

.setFillColor_(Color.white)

.nodeDownAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])})

.nodeTrackAction_({arg nodeloc; nodeloc.postln; Synth(\pure, [\freq, n[nodeloc[1]][nodeloc[0]]])})

.setBackgrDrawFunc_({

19.do({arg i;

if((i==1)||(i==2)||(i==4)||(i==5)||(i==7)||(i==9)||

(i==10)||(i==12)||(i==13)||(i==15)||(i==16)||(i==18),{

Color.new255(0, 0, 0, 120).set;

},{

Color.new255(255, 255, 255, 120).set;

});

Pen.fillRect(Rect(23+(i*(723/nTET)), 0, 580/nTET, 380));

})

});

)