ParaSpace a GUI parameter space

ParaSpace is a GUI widget, similar to the SCEnvelopeView, but has some

additional functionality and it is easier to use. One can select many nodes

at the same time and drag them around.

>> Use ctrl+click to create a node <<

>> Draw a rect around one or more nodes to select them <<

>> Use backspace to delete a selected node <<

>> Hold down 'c' to connect (a) selected node(s) to another node <<

*new(window, bounds);

window - A SCWindow in which the ParaSpace will appear

bounds - The Rect of the ParaSpace

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

nodeTrackAction_ - The function that dragging the node will trigger.

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

nodeOverAction_ - The function that mouse over on a node will trigger. (sets acceptsMouseOver)

keyDownAction_ - The function that key down on the view will trigger. (makes view focusable)

createNode(x, y, color) - Creates a node in loc x and y. Color is optional.

createConnection(a, b) - Creates a connection between node a and b.

deleteNode(index) - Deletes a specific node.

deleteConnection(a, b) - Deletes a connection between two nodes.

deleteConnections - Deletes all connections.

clearSpace - Clears the View.

setNodeSize_(int) - The size of ALL paranodes.

setNodeSize_(int, int) - The size of an individual node. (index, size)

setFillColor_(color) - The color of all the nodes. (see setNodeColor)

setShape_(shape) - Shape can be either "rect" or a "circle".

setOutlineColor(color) - Set the outline color of the selected nodes.

getNodeLoc(index) - Get the x, y pixel location of a node returned in an [x, y] array.

getNodeLoc1(index) - Get the x, y normalized (0 to 1) location returned in an [x, y] array.

setNodeLoc_(index, x, y) - Set the x, y pixel location of a node.

setNodeLoc1_(index, x, y) - Set the x, y location normalized (0 to 1) location.

setNodeString_(index, string) - Set the label/string of a node.

getNodeString_(index) - Get string of a node.

setFont_(font) - Set the font..

setFontColor_(Color) - Set the color of the font.

setNodeStates_(array) - Updates the grid from a 2 dimensional array of :

- nodes, connections, color and size.

- You can pass nil as either nodes or connections to update either.

getNodeStates - Returns 2 dimensional array of nodes and connections.

setNodeColor_(index, Color) - Sets the color of an individual node.

getNodeColor(index) - Returns the color of an individual node.

setBackgrColor_(Color) - The background color of the view.

setBackgrDrawFunc_(func) - Allows Pen to draw behind the nodes and connections.

setSelectFillColor_(Color) - The color of the rect when selecting nodes.

setSelectStrokeColor_(Color) - The stroke color of the rect when selecting nodes.

setNodeLoc1Action_(index, x, y, \action) - Set location and perform an action (\up, \down, \track).

setNodeLocAction_(index, x, y, \action) - Set location and perform an action (\up, \down, \track).

NOTE: all GUI updates, such as setNodeLoc() and createConnection() have a default

argument to refresh the window after the new state has been set. If you are using

these methods in a routine with loads of updating, you might want to set refresh

to false setNodeLoc_(i, x, y, false) and then do a.refresh; after the routine, so

you aren't updating on every iteration of the routine, but at the end of the loop.

// choose a gui (see GUI helpfile)

GUI.swing;

GUI.cocoa;

ParaSpace.new

// use ctrl+click to create a Box

(

w = GUI.window.new("ParaSpace", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

)

a.createNode(122,33)

a.deleteNode(0)

a.createNode(122,33)

a.setFillColor_(Color.new255(222.rand, 222.rand, 122.rand));

a.setBackgrColor_(Color.new255(120+(122.rand), 120+(122.rand), 120+(122.rand)));

a.setNodeColor_(0, Color.white);

a.setOutlineColor_(Color.red);

a.setNodeSize_(0, 14); // round to 2 due to drawing

a.getNodeSize(0);

a.createNode(222.rand,270.rand)

a.createConnection(0, 1);

a.deleteConnection(0, 1);

a.createConnection(0, 1);

a.createNode(22,133)

b = a.getNodeStates

a.clearSpace

a.setNodeStates_(b)

// location in pixels

a.getNodeLoc(0);

a.setNodeLoc_(0, 135, 120);

// location in floats from 0 to 1

a.getNodeLoc1(0);

a.setNodeLoc1_(0, 0.3, 0.36);

// select more than one box by dragging

a.setSelectFillColor_(Color.red(alpha:0.2));

a.setSelectStrokeColor_(Color.blue(alpha:0.8));

a.setMouseOverState_(true); // slightly cpu intensive

a.nodeOverAction_({arg node; "over".postln; node.spritenum.postln; node.nodeloc.postln;});

a.nodeOverAction_({nil}); // delete the action

a.setMouseOverState_(false);

a.nodeDownAction_({arg node; "down".postln; node.spritenum.postln; node.nodeloc.postln;});

a.nodeUpAction_({arg node; "up".postln; node.spritenum.postln; node.nodeloc.postln;});

(

a.nodeTrackAction_({arg node; "tracking sprite: ".post; node.spritenum.post;

" - location : ".post; node.nodeloc.postln;});

)

(

a.connectAction_({arg node1, node2;

"you connected node : ".post; node1.spritenum.post;

" and node : ".post; node2.spritenum.postln;

"node1 location : ".post; node1.nodeloc.postln;

"node2 location : ".post; node2.nodeloc.postln;

});

)

(

a.keyDownAction_({arg key, modifiers, unicode;

[\key, key, \modifiers, modifiers, \unicode, unicode].postln;

if(key == $a, {"You pressed the a key".postln;});

});

)

(

a.setBackgrDrawFunc_({

10.do{

Color.blue(rrand(0.0, 1), rrand(0.0, 0.5)).set;

Pen.addWedge(200@150, rrand(10, 100), 2pi.rand, 2pi.rand);

Pen.perform([\stroke, \fill].choose);

}

});

)

// or perhaps?

(

a.setBackgrDrawFunc_({

20.do{|i| Pen.line(40.5+(i*20)@20, 40.5+(i*20)@280)};

14.do{|i| Pen.line(20@(40.5+(i*20)), 380@40.5+(i*20))};

});

)

a.setShape_("circle")

a.clearSpace

///////////// example 2

(

var bounds;

bounds = Rect(20, 20, 760, 520);

w = GUI.window.new("ParaSpace", Rect(10, 500, 800, 612)).front;

a = ParaSpace.new(w, bounds: bounds);

20.do({

a.createNode(bounds.width.rand, bounds.height.rand, refresh:false);

});

30.do({

a.createConnection(20.rand, 20.rand, refresh:false);

});

20.do({arg i;

a.setNodeColor_(i, Color.new255(100+(155.rand), 100+(155.rand), 100+(155.rand)), refresh:false);

});

20.do({arg i;

a.setNodeSize_(i, (6+(12.rand)).round(2), refresh:false);

});

a.refresh;

)

b = a.getNodeStates

a.clearSpace

a.setNodeStates_(b)

///////////// example 3

(

w = GUI.window.new("ParaSpace", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

a.setShape_("circle");

36.do({arg i;

a.createNode(3+(i*10), 130, refresh:false)

});

35.do({arg i;

a.createConnection(i, i+1, refresh:false);

});

a.setBackgrDrawFunc_({

10.do{

GUI.pen.color = Color.green(rrand(0.0, 1), rrand(0.0, 0.5));

GUI.pen.addWedge(200@150, rrand(10, 100), 2pi.rand, 2pi.rand);

GUI.pen.perform([\stroke, \fill].choose);

}

})

)

(

t = Task({

inf.do({arg i;

36.do({arg j;

a.setNodeLoc_(j, 3+((j%36)*10), ((i*j/100).sin*120)+130, false); // false

});

0.1.wait;

a.refresh; // Updating GUI

})

}, SystemClock);

t.start;

)

t.stop;

///////////// example 4

SynthDef(\sin, {arg freq, amp=1;

Out.ar(0, SinOsc.ar(Lag3.kr(freq, 0.1), 0, amp)!2);

}).load(s);

(

l = List.new;

w = GUI.window.new("ParaSpace", Rect(10, 500, 800, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 760, 260));

76.do({arg i;

a.createNode(3+(i*10), 130, refresh:false);

l.add(Synth(\sin, [\freq, 400, \amp, 0.01]));

});

75.do({arg i;

a.createConnection(i, i+1, refresh:false);

});

a.refresh;

)

(

t = Task({

var d;

inf.do({arg i;

76.do({arg j;

d = ((i*(j/100)).sin*120)+130;

a.setNodeLoc_(j, 3+((j%76)*10), d, false); // not updating GUI

l[j].set(\freq, 400+(400 - (d*4)));

});

0.05.wait;

a.refresh; // Here we update the GUI.

})

}, AppClock);

t.start;

)

t.stop;

///////////// example 5 - where sound happens on connections

(

SynthDef(\sinWEnv, {arg freq, amp=0.01;

Out.ar(0, SinOsc.ar(freq, 0, amp)!2 * EnvGen.ar(Env.perc(0.01,0.4), doneAction:2));

}).load(s);

w = GUI.window.new("ParaSpace", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

36.do({arg i; a.createNode(350.rand, 250.rand, refresh:false)});

a.connectAction_({arg node1, node2;

Synth(\sinWEnv, [\freq, 400+ (400-node1.nodeloc.y)]);

Synth(\sinWEnv, [\freq, 400+ (400-node2.nodeloc.y)]);

});

t = Task({

1.wait;

inf.do({arg i;

6.do({arg j; a.createConnection(35.rand, 35.rand, false);});

176.do({arg j; a.deleteConnection(35.rand, 35.rand, false);});

0.6.coin(

a.setNodeLoc1_(35.rand, 1.0.rand, 1.0.rand, false);

a.setNodeColor_(35.rand, Color.new255(255.rand, 255.rand, 255.rand), false);

);

a.refresh;

0.05.wait;

})

}, AppClock);

t.start;

)

t.stop;

//////////////// example 6

SynthDef(\sinWEnv, {arg freq, amp=0.03;

Out.ar(0, SinOsc.ar(freq, 0, amp)!2 * EnvGen.ar(Env.perc(0.01,0.4), doneAction:2));

}).load(s);

(

var bounds, oldcolor, freqList;

bounds = Rect(20, 20, 310, 310);

w = GUI.window.new("ParaSpace", Rect(10, 600, 350, 350)).front;

a = ParaSpace.new(w, bounds: bounds);

a.setNodeSize_(16);

a.setFillColor_(Color.new255(108, 143, 103, 80));

a.nodeDownAction_({arg node;

oldcolor = node.color;

node.color = Color.new255(188, 143, 143, 190);

});

a.nodeUpAction_({arg node; node.color = oldcolor});

// not very interesting scale mapping... all random freqs (feel free to map boxes to freqs)

freqList = Array.fill(225, {200 + (5000.rand)});

15.do({arg i;

15.do({arg j;

a.createNode(14+(i*20), 14+(j*20), refresh:false);

});

});

a.refresh;

t = Task({

inf.do({

a.deleteConnections(false); // not updating GUI

a.setFillColor_(Color.new255(108, 143, 103, 80), false);

20.do({r = 225.rand;

a.createConnection(112, r, false);

a.setNodeColor_(r, Color.yellow(alpha:0.6), false);

f = ((1-a.getNodeLoc1(112)[1])*2) * freqList[r];

Synth(\sinWEnv, [\freq, f]);

});

a.setNodeColor_(112, Color.green(alpha:0.6), false);

a.refresh;

0.45.wait;

});

}, SystemClock);

t.start;

)

t.stop;

///////////////// example 7

// Displaying data in the ParaSpace

(

var data;

w = GUI.window.new("ParaSpace", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

// what's that data?

data = [["2000", Color.red, 0.3, 0.5], ["2002", Color.green, 0.4, 0.6], ["2004", Color.blue, 0.8, 0.2], ["2006", Color.yellow, 0.37, 0.8]];

data.do({arg d, i; a.createNode1(d[2], d[3], d[1]).setNodeString_(i, d[0])});

(data.size-1).do({arg i; a.createConnection(i, i+1)});

a.setBackgrDrawFunc_({

18.do{|i| Color.blue(alpha:0.2).set;

Pen.line(40.5+(i*20)@20, 40.5+(i*20)@280); Pen.stroke};

13.do{|i| Color.blue(alpha:0.2).set;

Pen.line(20@(40.5+(i*20)), 380@(40.5+(i*20))); Pen.stroke};

});

)

b = a.getNodeStates

a.clearSpace

a.setNodeStates_(b)

/////////////// example 8

// If example 7 was boring, here is one with sound

// first load the synthdef

(

SynthDef(\ps, {|freq=300, sinamp=0.4, modfreq=20, modamp=100, bpfreq=300, bprq=1, vol=0.5, pan=0|

var carrier, modulator, bpf;

modulator = SinOsc.ar(modfreq, 0, modamp);

carrier = SinOsc.ar(freq+modulator, 0, sinamp);

bpf = BPF.ar(carrier, bpfreq, bprq);

Out.ar(0, Pan2.ar(bpf, pan)*vol);

}).load(s);

)

(

var data;

w = GUI.window.new("ParaSpace Synth", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

data = [["carrier", Color.red, 0.3, 0.5], ["modulator", Color.green, 0.4, 0.6], ["BPF", Color.blue, 0.8, 0.2], ["vol/pan", Color.yellow, 0.37, 0.8]];

data.do({arg d, i; a.createNode1(d[2], d[3], d[1]).setNodeString_(i, d[0])});

(data.size-1).do({arg i; a.createConnection(i, i+1)});

d = Synth(\ps);

a.nodeTrackAction_({arg node;

switch (node.spritenum)

{0} {d.set(\freq, (280-node.nodeloc.y)*3); d.set(\sinamp, node.nodeloc.x/360)}

{1} {d.set(\modfreq, (280-node.nodeloc.y)*1.6); d.set(\modamp, node.nodeloc.x)}

{2} {d.set(\bpfreq, node.nodeloc.x*3); d.set(\bprq, node.nodeloc.y/360)}

{3} {d.set(\vol, 1-(node.nodeloc.y/260)); d.set(\pan, ((node.nodeloc.x/400)-0.5)*2)};

});

)

a.setNodeLoc1Action_(0, 1.0.rand, 1.0.rand, \track)

////////////////// example 9

// constraining the x location (sliders) using the temp variable of the node.

(

var data;

w = GUI.window.new("ParaSpace", Rect(10, 500, 400, 300)).front;

a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));

10.do({arg d, i; a.createNode1(i/10+0.03, 0.5).setNodeString_(i, (i+1).asString);

a.paraNodes[i].temp = i/10+0.03;

});

9.do({arg i; a.createConnection(i, i+1)});

a.setBackgrDrawFunc_({

9.do{|i| Color.blue(alpha:0.2).set;

Pen.line(52.5+(i*36)@20, 52.5+(i*36)@280); Pen.stroke};

12.do{|i| Color.blue(alpha:0.2).set;

Pen.line(20@(40.5+(i*20)), 380@(40.5+(i*20))); Pen.stroke};

});

a.nodeTrackAction_({arg node;

a.setNodeLoc1_(node.spritenum, node.temp, a.getNodeLoc1(node.spritenum)[1])

});

)