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 = Window.new("ParaSpace", Rect(10, 500, 400, 300)).front;
a = ParaSpace.new(w, bounds: Rect(15, 15, 360, 260));
)
a.createNode(180,130)
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(180@130, rrand(10, 100), 2pi.rand, 2pi.rand);
Pen.perform([\stroke, \fill].choose);
}
});
)
// or perhaps?
(
a.setBackgrDrawFunc_({
20.do{|i| Pen.line((i*20)@0, (i*20)@280)};
14.do{|i| Pen.line(0@(i*20), 380@(i*20))};
});
)
a.setShape_("circle")
a.clearSpace
///////////// example 2
(
var bounds;
bounds = Rect(20, 20, 760, 520);
w = Window.new("ParaSpace", Rect(10, 500, 800, 612)).front;
a = ParaSpace.new(w, bounds: bounds);
20.do({
a.createNode(740.rand, 500.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 = 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(190@130, 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, ((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);
}).add;
(
l = List.new;
w = 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));
}).add;
(
w = 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));
}).add;
(
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 = 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((i*20)@0, (i*20)@280); Pen.stroke};
13.do{|i| Color.blue(alpha:0.2).set;
Pen.line(0@((i*20)), 380@((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);
}).add;
)
(
var data;
w = 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 = Window.new("ParaSpace", Rect(10, 500, 400, 300)).front;
a = ParaSpace.new(w, bounds: Rect(20, 20, 360, 260));
9.do({arg d, i; a.createNode1((i/10)+0.1, 0.5).setNodeString_(i, (i+1).asString);
a.paraNodes[i].temp = (i/10)+0.1;
});
8.do({arg i; a.createConnection(i, i+1)});
a.setBackgrDrawFunc_({
10.do{|i| Color.blue(alpha:0.2).set;
Pen.line((i*36)@0, (i*36)@280); Pen.stroke};
13.do{|i| Color.blue(alpha:0.2).set;
Pen.line(0@(i*20), 380@(i*20)); Pen.stroke};
});
a.nodeTrackAction_({arg node;
a.setNodeLoc1_(node.spritenum, node.temp, a.getNodeLoc1(node.spritenum)[1])
});
)