Runner handler of GeoGraphy process

Inherits from: Object

The Runner class handles all the dynamic processes on a Graph, i.e. it allows to instatiate Actants and to control them. Typically, you never instatiate directly Actant, as in that case it is not possible to coordinate them in any way.

See also: Graph, Actant

Fundamentals

At its core Runner simply handles Actants, so that you can set a common tempo for all, count vertex occurences, create a unified GUI, etc.

Note: GUIs are always deferred, so you can use SystemClock in routines involving graphics.

Creation / Class Methods

*new (graph, bpm, weight, name)

Create a new Runner instance.

graph - a Graph object. Default value is nil.

bpm - tempo expressed in bpm. Default value is 60, e.g. if you have a duration of 1 in the edge this means that it will last 1 second. I.e. with default, duration is expressed in seconds.

weight - weight is a value associated to an Actant. As actants can be thought as tracks, it is an abstraction for determining relations between actant. Here it is only used as the default associated to actants created thorugh runnner. Default value is 0.1.

offsetWeight - a weight proper to the runner. Default value is 0.0.

name - a symbolic name for tracing the runner

(

g = Graph.new

.addVertex(600, 400, 0, "bump")

.addEdge(1, 1, 1) ;

r = Runner.new(g);

r.graph.graphDict.postln ;

)

Accessing Instance and Class Variables

actantDict_(arg1)

actantDict

An IdentityDictionary collecting all the actants instantiated by the Runner, so that it is possible to refer to them buy an ID.

See Actant.

bpm_(arg1)

bpm

Time in GeoGraphy is referred to a musical tempo (beats per minute).

Default value is 60, so that all durations can represent seconds.

graph_(arg1)

graph

A graph object used by Runner. See Graph.

weight_(arg1)

weight

A default weight for newly instantiated actants.

Default value is 0.1. See Actant.

offsetWeight_(arg1)

offsetWeight

A weight associated to the runner.

Default value is 0.1. See Actant.

statsDict

A dictionary counting for each vertex its occurences while actants are running.

This must be done at Runner level, because there can be many actants passing through the same vertex, so total occurences cannot be counted at Actant level.

na

Increasing number of actants, used for intern reference.

Functionality

The following are the most relevant methods. Still the class is a bit messy, so many methods are left without documentation purposely.

write (path)

Save to file a Runner object in archive form. Actually it saves two files, one with .run extension and one with .gra extension.

path - the path where to find the files. A name without extension should be provided as extensions are appended by the method.

(

g = Graph.new

.addVertex(600, 400, 0, "bump")

.addEdge(1, 1, 1) ;

g.plot ;

r = Runner.new(g);

r.addAndSetup(1) ;

r.write("sounds/test") //write to SCapp/sounds

)

open (path)

Open a .run file and a .gra file with the same name.

path - path to the run file.

(

g = Graph.new ;

r = Runner.new(g) ;

r.open("sounds/test.run") // open test.run and test.gra

g.plot ;

)

addAndSetup (aStartingVertexID, times)

The general method to add an actant.

aStartingVertexID - the vertex ID where the actant starts to walk on the graph. Default value is nil.

times - the lenght of the path on the graph, expressed in number of vertices. Default value is nil, which is then converted to inf.

s = Server.default.boot ;

(

g = Graph.new ;

r = Runner.new(g, weight:0.03) ;

p = Painter.new(g, r) ;

Routine.new({

30.do({ arg i ;

g.addVertex((100+1000).rand, (100+600).rand, 0, "s"++(70+i).asString) ;

g.addEdge(g.nv-1, g.nv-1, 2.0.rand) ;

r.addAndSetup(g.nv-1).start(r.na-1) ;

1.wait

})

}).play(SystemClock) ;

)

// adding some noise. You can evaluate while routine is working

h = Sinusoider.new(r) ;

r.gui ; // if want to see what's going on

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

// another ex

(

// using times

g = Graph.new ;

r = Runner.new(g, weight:0.02).gui ;

p = Painter.new(g, r) ;

h = Sinusoider.new(r) ;

Routine.new({

50.do({ arg i ;

g.addVertex((100+1000).rand, (100+600).rand, 0, "s"++(70+i).asString) ;

g.addEdge(g.nv-1, g.nv-1, rrand(0.1, 0.5)) ;

r.addAndSetup(g.nv-1, 20).start(r.na-1) ;

0.5.wait

})

}).play(SystemClock) ;

)

removeActant (aID)

Remove the actant (after stopping if necessary).

aID - the ID of the required actant in actandDict. Default value is nil.

s = Server.default.boot ;

(

g = Graph.new ;

r = Runner.new(g, weight:0.02).gui ;

p = Painter.new(g, r) ;

h = Sinusoider.new(r) ;

Routine.new({

20.do({ arg i ;

g.addVertex((100+1000).rand, (100+600).rand, 0, "s"++(30+(i*5)).asString) ;

g.addEdge(g.nv-1, g.nv-1, rrand(0.5, 1)) ;

r.addAndSetup(g.nv-1).start(r.na-1) ;

1.wait

}) ;

20.do({ arg i ;

r.removeActant(r.na-i) ;

2.wait ;

})

}).play(SystemClock) ;

)

setWeight (aID, weight)

Short prose description of method.

aID - an actant ID. Default value is nil.

weight - the weight of the actant. Default value is nil.

s = Server.default.boot ;

(

// random mixing

g = Graph.new ;

r = Runner.new(g, weight:0.01).gui(step: 20) ;

p = Painter.new(g, r) ;

h = SlicePlayer(r).initAudio("sounds/a11wlk01.wav") ;

40.do({ arg i;

g.addVertex((100+1000).rand, (100+600).rand, 0, "p"++i.asString) ;

g.addEdge(g.nv-1, g.nv-1, rrand(0.1, 1)) ;

r.addAndSetup(g.nv-1) ;

}) ;

)

r.startAll ;

(

// automix

Routine.new({

inf.do({ arg i ;

r.setWeight(r.actantDict[r.actantDict.keys.choose], 0.5.rand ) ;

rrand(0.01, 0.25).wait ;

}) ;

}).play(SystemClock) ;

)

setOffsetWeight (offset)

Set the weight associated to the runner.

OffsetWeight is an abstraction for a general volume associated to a set of tracks.

offset - the new offsetWeight. Default value is 0.

start (aID)

pause (aID)

resume (aID)

stop (aID)

Control the actant (substantially it's a wrapper for a Task)

aID - the desired actant's ID. Default value is nil.

s = Server.default.boot ;

(

g = Graph.new ;

r = Runner.new(g).gui ;

Painter(g, r) ;

g.addVertex(100, 100, 0, "q80") ;

g.addVertex(100, 700, 0, "q83") ;

g.addVertex(1100, 700, 0, "q85") ;

g.addVertex(1100, 100, 0, "q86") ;

g.addEdge(1,2, 1) ;

g.addEdge(2,3, 1) ;

g.addEdge(3,3, 1/3) ;

g.addEdge(3,4, 1) ;

g.addEdge(4,4, 1/3) ;

g.addEdge(4,1, 1/3) ;

Squarer(r) ;

r.setTempo(480) ;

r.addAndSetup(1) ;

r.start(1) ;

)

(

Routine.new({

inf.do({ arg i;

r.pause(1) ;

"I have paused".postln ;

(2+1.0).rand.wait ;

r.resume(1) ;

"Now I start again from where I was".postln ;

(2+1.0.rand).wait ;

r.stop(1) ;

"STOP!".postln ;

(2+1.0.rand).wait ;

"from the beginning".postln ;

r.start(1) ;

(2+1.0.rand).wait ;

})

}).play(SystemClock)

)

startAll

pauseAll

resumeAll

stopAll

The same as before but to all the actants in one time.

s = Server.default.boot ;

(

//init stuff

g = Graph.new ;

r = Runner.new(g) ;

g.addVertex(1200.rand, 800.rand, 0, "q30") ;

k = 31 ;

60.do({ arg i ;

k = k+[1,2].choose ;

g.addVertex(1200.rand, 800.rand, 0, "q"++k.asString) ;

g.addEdge(g.nv-2, g.nv-1, [1, 1+1/3, 4/5].choose) ;

if ([true, false].choose, {g.addEdge(g.nv-1, g.nv-1, [1/3, 1/5].choose) }) ;

}) ;

g.addEdge(61, 1, 1) ;

// It's better to paint AFTER it has been generated

// otherwise refreshing kills...

// remember that you can live the same without a Painter

Painter(g, r) ; // just comment it out

// g.plot ; // in case you just wnat to see the topology

r.addAndSetup(1) ;

r.addAndSetup(10) ;

r.addAndSetup(20) ;

r.addAndSetup(30) ;

)

(

// audio

Squarer(r) ;

r.setTempo(500) ;

r.start(1) ;

r.start(2) ;

r.start(3) ;

r.start(4) ;

)

(

// scheduling

Routine.new({

inf.do({ arg i;

r.pauseAll ;

"I have paused".postln ;

(0.1+0.5).rand.wait ;

r.resumeAll ;

"Now I start again from where I was".postln ;

(0.1+0.5.rand).wait ;

r.stopAll ;

"STOP!".postln ;

(0.1+0.5.rand).wait ;

"from the beginning".postln ;

r.startAll ;

(0.1+0.5.rand).wait ;

})

}).play(SystemClock)

)

setTempo (aBpm)

Set a general tempo in bpm. The tempo is the number of unit in time.

So if tempo == 60, then a duration of 1 unit is a second.

aBpm - tempo in bpm. Default value is nil.

Server.local.boot ;

(

var graph, runner, bpm, gui ;

// you can evidently have more graphs, runners, etc

// each one can receive different values e.g. for tempo

5.do({ arg i ;

graph = Graph.new ;

runner = Runner.new(graph, name: ("runner "++i).asSymbol) ;

gui = RunnerGUI(runner, controlNum: 2) ;

gui.w.bounds_(gui.w.bounds.moveBy(0, i*210.neg)) ;

Sinusoider(runner) ;

graph.addVertex(1200.rand, 800.rand, 0, "s"++(80+(i*[1,2,3].choose)).asString) ;

graph.addEdge(1,1, 1) ;

runner.addAndSetup(1) ;

runner.start(1) ;

Routine.new({

inf.do({

bpm = rrand(i*20+40, i*20+200) ;

runner.setTempo(bpm) ;

rrand(0.5, 1).wait ;

})

}).play(SystemClock) ;

})

)

gui (step)

Create a gui control for all the Runner's actants by instantiating a RunnerGUI object.

The first row shows the runner's name and a slider controlling the offsetWeight.

Then each actant is given a start/stop button, a label for the actual vertex, a slider for the weight.

step - a multiplier for scaling the gui. Default value is 35.

(

var step ;

// changing the step

g = Graph.new ;

r = Runner.new(g) ;

g.addVertex(1200.rand, 800.rand, 0, "") ;

r.addAndSetup(1) ;

Routine.new({

100.do({ arg i ;

step = i+10 ;

// RunnerGUI is what Runner calls

// step is the step arg

v = RunnerGUI.new(r, step, 100, 400) ;

0.5.wait ;

v.w.close ;

})

}).play(SystemClock)

)

Examples

// A variation on the random mixing example exploiting the

// SlicePlayer spatial mapping

s = Server.local.boot ;

(

g = Graph.new ;

r = Runner.new(g, weight:0.2).gui ;

p = Painter.new(g, r) ;

h = SlicePlayer(r).initAudio("sounds/a11wlk01.wav") ;

20.do({ arg i;

g.addVertex(100+100.rand, 100+100.rand, 0, "p"++i.asString) ;

g.addEdge(g.nv-1, g.nv-1, rrand(0.1, 1)) ;

r.addAndSetup(g.nv-1) ;

}) ;

)

r.startAll ;

// moving around the cloud

(

var v, x, y;

Routine.new({

inf.do({ arg i ;

v = g.graphDict.keys.choose ;

x = g.graphDict[v][0];

y = g.graphDict[v][1] ;

r.graph.changeVertexPosition(v, (x+50)%1200, (y+50)%800) ;

rrand(0.01, 0.25).wait ;

}) ;

}).play(SystemClock) ;

)

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

// Another looping cloud

(

g = Graph.new ;

r = Runner.new(g, weight:0.2).gui ;

p = Painter.new(g, r) ;

h = SlicePlayer(r).initAudio("sounds/a11wlk01.wav") ;

20.do({ arg i;

g.addVertex(550+100.rand, 350+100.rand, 0, "p"++i.asString) ;

g.addEdge(g.nv-1, g.nv-1, rrand(0.1, 1)) ;

r.addAndSetup(g.nv-1) ;

}) ;

)

r.startAll ;

// move it

(

var v, x, y;

Routine.new({

inf.do({ arg i ;

v = g.graphDict.keys.choose ;

x = g.graphDict[v][0];

y = g.graphDict[v][1] ;

r.graph.changeVertexPosition(v, (x+rrand(-50,50))%1200, (y+rrand(-50,50))%800) ;

rrand(0.01, 0.25).wait ;

}) ;

}).play(SystemClock) ;

)

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

// A third one

// No mercy. sooner or later it will crash...

// (oh, but SC is damn' efficient, so you need many minutes)

Server.local.boot ;

(

g = Graph.new ;

r = Runner.new(g, weight:0.6).gui ;

p = Painter.new(g, r) ;

// how many as you want

// note that this means they are in synch

// if you want parallelism instantiate another Runner

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/chalumeau or.wav") ;

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/ocarina or.wav") ;

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/quena or.wav") ;

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/fl circolo or.wav") ;

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/fl quadro or.wav") ;

SlicePlayer(r).initAudio("/audioRumentario/sources/fiati/coulisse or.wav") ;

g.addVertex(575+50.rand, 375+50.rand, 0, "p"++0.asString) ;

19.do({ arg i;

g.addVertex(550+100.rand, 350+100.rand, 0, "p"++(i+1).asString) ;

g.addEdge(g.nv-2, g.nv-1, rrand(0.01, 0.1)) ;

}) ;

g.addEdge(g.nv-1, 1, rrand(0.01, 0.1)) ;

r.addAndSetup(1) ;

)

r.start(1) ;

(

var v, x, y, j;

Routine.new({

inf.do({ arg i ;

v = g.graphDict.keys.choose ;

x = g.graphDict[v][0];

y = g.graphDict[v][1] ;

j = i*0.1 ;

r.graph.changeVertexPosition(v, (x+rrand(-50-j,50+j))%1200, (y+rrand(-50-j,50+j))%800) ;

// Now you know...

r.setTempo(r.bpm*0.001+r.bpm) ;

rrand(0.01, 0.25).wait ;

}) ;

}).play(SystemClock) ;

)