472 lines
11 KiB
Text
472 lines
11 KiB
Text
|
#lang scribble/manual
|
||
|
@(require (for-label racket))
|
||
|
|
||
|
@title{Plotter}
|
||
|
Plot numerical data on a window or view@section{categories}
|
||
|
GUI>Accessories
|
||
|
@section{related}
|
||
|
Reference/plot
|
||
|
|
||
|
@section{description}
|
||
|
|
||
|
Plot data of up to three dimensions on a link::Classes/Window:: or link::Classes/UserView::.
|
||
|
|
||
|
@section{subsection}
|
||
|
Keyboard shortcuts
|
||
|
|
||
|
When the plotter window has focus, the following keyboard shortcuts can be used to change the display:
|
||
|
@section{table}
|
||
|
|
||
|
## + / - || vertical zoom
|
||
|
## = || compare plot channels
|
||
|
## n || toggle normalize display (0..1) / (-1..1), or fit range
|
||
|
## s || toggle superposition (see: superpose)
|
||
|
## m || switch plot mode (see: link::Classes/Plotter#-plotMode::)
|
||
|
## e || toggle editing (see: link::Classes/Plotter#-editMode::)
|
||
|
## g || toggle horizontal (domain) grid
|
||
|
## G || toggle vertical (codomain) grid
|
||
|
## p || print curve
|
||
|
## ctrl-+ / - || zoom font
|
||
|
## alt-click || post value
|
||
|
::
|
||
|
|
||
|
@section{subsection}
|
||
|
Method extensions
|
||
|
Plotter extends other classes with methods. To see what classes implements plot, see link::Overviews/Methods#plot::
|
||
|
|
||
|
@section{method}
|
||
|
plot (args)
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
// plot array
|
||
|
[1, 6, 2, -5, 2].plot;
|
||
|
(0..100).normalize(0, 8pi).sin.plot;
|
||
|
|
||
|
// nested arrays
|
||
|
{ (0..100).normalize(0, 15.0.rand).sin }.dup(3).plot;
|
||
|
{ { (0..17).normalize(0, 15.0.rand).sin }.dup(4) }.dup(3).plot;
|
||
|
|
||
|
// UGen functions
|
||
|
{ SinOsc.ar([700, 357]) * SinOsc.ar([400, 476]) * 0.2 }.plot;
|
||
|
|
||
|
// Buffer
|
||
|
Buffer.read(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff").plot;
|
||
|
|
||
|
// Env
|
||
|
Env.perc(0.4, 0.6).plot;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
plotGraph (n,from,to,...)
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
{ |x| sin(x) }.plotGraph(300,0,2*pi);
|
||
|
{ |x| sin(1/x)*x }.plotGraph(from:0.0001,to:0.2);
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{section}
|
||
|
Changing global defaults
|
||
|
|
||
|
The default styles are kept (and may be overridden) in
|
||
|
@racketblock[GUI.skin.at(\plot)::. See also link::Classes/GUI:: help.
|
||
|
|
||
|
]
|
||
|
|
||
|
@racketblock[
|
||
|
// specify plot layout
|
||
|
(
|
||
|
GUI.skin.plot.gridLinePattern = FloatArray[1, 0];
|
||
|
GUI.skin.plot.fontColor = Color(0.5, 1, 0);
|
||
|
GUI.skin.plot.gridColorX = Color.yellow(0.5);
|
||
|
GUI.skin.plot.gridColorY = Color.yellow(0.5);
|
||
|
GUI.skin.plot.background = Color.black;
|
||
|
GUI.skin.plot.plotColor = (10..0).normalize(0.1, 1).collect { |i| Color.rand(i) };
|
||
|
GUI.skin.plot.labelX = "X";
|
||
|
GUI.skin.plot.labelY = "Y";
|
||
|
);
|
||
|
|
||
|
(
|
||
|
x = { |i| (0..60).scramble.clump(8) * (3.5 ** i) }.dup(3);
|
||
|
x.plot("ARRAY:PLOT", Rect(200, 300, 600, 500));
|
||
|
)
|
||
|
|
||
|
GUI.skin.plot.put(\plotColor, { Color.rand(0.0, 0.8) } ! 8);
|
||
|
[(0..100), (20..120), (40..140)].squared.flop.bubble.plot;
|
||
|
|
||
|
// reset the defaults:
|
||
|
Plot.initClass;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{classmethods}
|
||
|
|
||
|
|
||
|
@section{method}
|
||
|
new
|
||
|
@section{argument}
|
||
|
name
|
||
|
Plot window title.
|
||
|
@section{argument}
|
||
|
bounds
|
||
|
The window bounds (a link::Classes/Rect::).
|
||
|
@section{argument}
|
||
|
parent
|
||
|
Either a link::Classes/Window:: / link::Classes/View:: may be passed in - then the plot is embedded. Otherwise a new link::Classes/Window:: is created.
|
||
|
@section{discussion}
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
(
|
||
|
a = Plotter("the plot", Rect(600, 30, 800, 250));
|
||
|
a.value = (0..1000).normalize(0, 14pi).curdle(0.01).scramble.flat.sin;
|
||
|
)
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{instancemethods}
|
||
|
|
||
|
|
||
|
@section{subsection}
|
||
|
Accessing Instance Variables
|
||
|
|
||
|
@section{method}
|
||
|
makeWindow
|
||
|
Open given plotter in a new window or within a given composite view.
|
||
|
@section{argument}
|
||
|
argParent
|
||
|
Either a link::Classes/Window:: or link::Classes/View:: may be passed in - then the plot is embedded. Otherwise a new link::Classes/Window:: is created.
|
||
|
@section{argument}
|
||
|
argBounds
|
||
|
The window bounds (a link::Classes/Rect::).
|
||
|
|
||
|
@section{method}
|
||
|
plotMode
|
||
|
Set the style of data display.
|
||
|
@section{argument}
|
||
|
symbol
|
||
|
Available modes:
|
||
|
@section{table}
|
||
|
|
||
|
##
|
||
|
@racketblock[\linear:: || connecting data points with linear interpolation
|
||
|
## ]
|
||
|
|
||
|
@racketblock[\points:: || draw data points only
|
||
|
## ]
|
||
|
|
||
|
@racketblock[\plines:: || combination of lines and points
|
||
|
## ]
|
||
|
|
||
|
@racketblock[\levels:: || horizontal lines
|
||
|
## ]
|
||
|
|
||
|
@racketblock[\steps:: || connecting data points with step interpolation
|
||
|
::
|
||
|
]
|
||
|
@section{discussion}
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
a = (0..20).scramble.plot;
|
||
|
a.plotMode = \points; a.refresh;
|
||
|
a.plotMode = \plines; a.refresh;
|
||
|
a.plotMode = \levels; a.refresh;
|
||
|
a.plotMode = \steps; a.refresh;
|
||
|
a.plotMode = \linear; a.refresh;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
setProperties
|
||
|
Set properties of all plot views. Defaults are taken from
|
||
|
@racketblock[GUI.skin.at(\plot);::
|
||
|
]
|
||
|
@section{argument}
|
||
|
... pairs
|
||
|
A list of symbol,value pairs. Supported properties:
|
||
|
@section{list}
|
||
|
|
||
|
## font
|
||
|
## fontColor
|
||
|
## gridColorX
|
||
|
## gridColorY
|
||
|
## plotColor (an link::Classes/Array::)
|
||
|
## backgroundColor
|
||
|
## gridLinePattern
|
||
|
## gridLineSmoothing ( link::Classes/Boolean:: )
|
||
|
## labelX
|
||
|
## labelY
|
||
|
## gridOnX ( link::Classes/Boolean:: )
|
||
|
## gridOnY ( link::Classes/Boolean:: )
|
||
|
::
|
||
|
@section{discussion}
|
||
|
|
||
|
Example:
|
||
|
|
||
|
@racketblock[
|
||
|
(
|
||
|
a = { (0..30).scramble }.dup(2).plot;
|
||
|
a.setProperties(
|
||
|
\fontColor, Color.red,
|
||
|
\plotColor, Color.blue,
|
||
|
\backgroundColor, Color.black,
|
||
|
\gridColorX, Color.white,
|
||
|
\labelX, "Humidity"
|
||
|
);
|
||
|
a.refresh;
|
||
|
);
|
||
|
|
||
|
GUI.skin.at(\plot); // defaults
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
editMode
|
||
|
If the edit mode is set to true, the data may be edited via cursor.
|
||
|
|
||
|
@racketblock[
|
||
|
a = (0..20).plot;
|
||
|
a.editMode = true; // now edit the data by clicking into the plot..
|
||
|
a.value; // the value
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
resolution
|
||
|
Set the number of data points displayed maximally per pixel (default: 1)
|
||
|
|
||
|
@racketblock[
|
||
|
a = (0..200).scramble.plot;
|
||
|
a.resolution = 8; a.refresh; // resizing the window shows interpolation
|
||
|
a.resolution = 1; a.refresh;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
findSpecs
|
||
|
If true (default:
|
||
|
@racketblock[true::), specs are derived from new data (using min and max values) automatically.
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
superpose
|
||
|
If set to true, plotter displays channels on top of each other (keyboard shortcut: s)
|
||
|
|
||
|
@racketblock[
|
||
|
a = { (0..30).scramble }.dup(2).plot;
|
||
|
a.superpose = true; a.refresh;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
value
|
||
|
Return or set the data values. Data may be numerical arrays of up to 3 dimensions.
|
||
|
|
||
|
@racketblock[
|
||
|
a = [1, 4, 2, 7, 4].dup(2).plot;
|
||
|
a.value;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
data
|
||
|
Reference to the current internal data.
|
||
|
|
||
|
@section{method}
|
||
|
cursorPos
|
||
|
@section{Returns}
|
||
|
the last cursorPos (a link::Classes/Point::).
|
||
|
|
||
|
@section{method}
|
||
|
plots
|
||
|
@section{Returns}
|
||
|
the single subplots (a link::Classes/Plot::).
|
||
|
|
||
|
|
||
|
@section{method}
|
||
|
specs
|
||
|
Set or get the spec for the y-axis (codomain).
|
||
|
|
||
|
@racketblock[
|
||
|
a = { (40..3000).scramble }.dup(2).plot;
|
||
|
a.specs = \freq.asSpec; a.refresh;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
domainSpecs
|
||
|
Set or get the spec for the x-axis (domain).
|
||
|
|
||
|
@racketblock[
|
||
|
a = { (40..300).scramble }.dup(2).plot;
|
||
|
a.domainSpecs = \freq.asSpec; a.refresh;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
editFunc
|
||
|
Supply a function which is evaluated when editing data. The function is called with the arguments:
|
||
|
@racketblock[plotter::, ]
|
||
|
|
||
|
@racketblock[plotIndex::, ]
|
||
|
|
||
|
@racketblock[index::, ]
|
||
|
|
||
|
@racketblock[val::, ]
|
||
|
|
||
|
@racketblock[x::, ]
|
||
|
|
||
|
@racketblock[y::.
|
||
|
]
|
||
|
@section{discussion}
|
||
|
|
||
|
Example:
|
||
|
|
||
|
@racketblock[
|
||
|
(
|
||
|
a = { (0..10).scramble.normalize }.dup(2).plot;
|
||
|
a.editMode = true;
|
||
|
a.editFunc = { |...args| args.postln };
|
||
|
);
|
||
|
|
||
|
// using plotter as a control interface
|
||
|
(
|
||
|
a = (0..10).scramble.normalize(300, 400).plot;
|
||
|
a.specs = \freq; a.plotMode = \points;
|
||
|
a.editMode = true;
|
||
|
x = { SinOsc.ar(\freq.kr(a.value)).mean * 0.1 }.play;
|
||
|
a.editFunc = { |plotter, plotIndex, i, val|
|
||
|
x.setn(\freq, a.value)
|
||
|
};
|
||
|
a.parent.onClose = { x.release };
|
||
|
);
|
||
|
|
||
|
(
|
||
|
a = { (0..10).scramble.normalize(300, 400) }.dup.plot;
|
||
|
a.specs = \freq; a.plotMode = \levels;
|
||
|
a.editMode = true;
|
||
|
x = {
|
||
|
var phase = SinOsc.ar(\rate.kr(a.value[1]));
|
||
|
SinOsc.ar(\freq.kr(a.value[0]), phase).mean * 0.1
|
||
|
}.play;
|
||
|
a.editFunc = { |plotter, plotIndex, i, val|
|
||
|
x.setn(\freq, a.value[0]);
|
||
|
x.setn(\rate, a.value[1]);
|
||
|
};
|
||
|
a.parent.onClose = { x.release };
|
||
|
);
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{examples}
|
||
|
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
// embedding in another GUI
|
||
|
(
|
||
|
w = Window("plot panel", Rect(20, 30, 520, 450));
|
||
|
Slider.new(w, Rect(10, 10, 490, 20)).resize_(2).action_ { |v|
|
||
|
a.value = (0..(v.value.linexp(0, 1, 5, 8000)).asInteger).scramble;
|
||
|
w.refresh;
|
||
|
};
|
||
|
z = CompositeView(w, Rect(10, 35, 490, 400)).background_(Color.rand(0.7)).resize_(5);
|
||
|
a = Plotter("plot", parent: z).value_([0, 1, 2, 3, 4].scramble * 100);
|
||
|
w.front;
|
||
|
)
|
||
|
|
||
|
|
||
|
(
|
||
|
a = Plotter("the plot", Rect(600, 30, 600, 400));
|
||
|
a.value = (0..100).normalize(0, 8pi).sin;
|
||
|
)
|
||
|
|
||
|
a.value = { |i| (0..90) % (i + 12) + ( (0..90) % (i + 2 * 1) ) }.dup(3);
|
||
|
a.value = (0..12).squared;
|
||
|
a.plotMode = \points; a.refresh;
|
||
|
a.plotMode = \levels; a.refresh;
|
||
|
a.plotMode = \plines; a.refresh;
|
||
|
|
||
|
a.domainSpecs = [[0, 115, \lin, 1]]; a.refresh;
|
||
|
|
||
|
a.parent.close; // close window
|
||
|
a.makeWindow; // open it again
|
||
|
|
||
|
a.value = { (0..70).scramble }.dup(3);
|
||
|
a.plotMode = \linear; a.refresh;
|
||
|
a.value = { |i| (0..2000).normalize(0, 4pi + i).sin } ! 4; // lots of values, test efficiency
|
||
|
a.value = { |i| (0..10000).normalize(0, 8pi + i).sin } ! 3; // lots of values, test efficiency
|
||
|
a.value = { (0..140).scramble } ! 7;
|
||
|
|
||
|
a.value = { |i| (0..90).normalize(0, 8pi + (i*2pi)).sin } ! 2 * [400, 560] + 700;
|
||
|
a.value = { |i| (_ + 2.0.rand).dup(100).normalize(0, 8pi + i).sin } ! 2 * 400 + 700;
|
||
|
|
||
|
|
||
|
// multi channel expansion of single values
|
||
|
a.value = { |i| (_ + 2.0.rand).dup(100).normalize(0, 8pi + i).sin *.t [1, 2, 3] } ! 2 * 400 + 700;
|
||
|
a.value = { |i| (0..10) **.t [1, 1.2, 1.3, 1.5] * (3.5 ** i) }.dup(3);
|
||
|
|
||
|
a.parent.bounds = Rect(400, 100, 500, 700);
|
||
|
a.parent.bounds = Rect(600, 30, 500, 300);
|
||
|
|
||
|
a.superpose = true;
|
||
|
a.value = { |i| (0..20) * (3.5 ** i) }.dup(5);
|
||
|
a.superpose = false;
|
||
|
|
||
|
// specs
|
||
|
|
||
|
a.value = (50..90).midicps.scramble;
|
||
|
a.specs = \freq; a.refresh;
|
||
|
a.value = (1..60).scramble.neg;
|
||
|
a.specs = \db; a.refresh;
|
||
|
|
||
|
a.value = { |i| { exprand(1e3, (10 ** (i + 8))) }.dup(90) }.dup(3);
|
||
|
a.value = { { exprand(1e3, 1e9) }.dup(90) }.dup(3);
|
||
|
a.specs = [[1e3, 1e10, \exp], [1e3, 1e20, \exp], [1e3, 1e30, \exp]]; a.refresh;
|
||
|
a.domainSpecs = [[0, 5], [-8, 100], [-1, 1]]; a.refresh;
|
||
|
|
||
|
|
||
|
// Array:plot
|
||
|
(
|
||
|
a = (4 ** (-5..0)).postln.plot;
|
||
|
a.specs = \delay; a.refresh;
|
||
|
a.domainSpecs = [0, 10, \lin, 0, 0, " Kg"].asSpec; a.refresh;
|
||
|
);
|
||
|
|
||
|
a.domainSpecs = [0.1, 10, \exponential, 0, 0, " Kg"].asSpec; a.refresh;
|
||
|
a.domainSpecs = [-10, 10, \lin, 0, 0, " Kg"].asSpec; a.refresh;
|
||
|
|
||
|
|
||
|
a = [(0..100) * 9, (200..1300) * 2, (200..1000)/ 5].plot;
|
||
|
a.superpose = true;
|
||
|
|
||
|
a = [[0, 1.2, 1.5], [0, 1.3, 1.5, 1.6], [0, 1.5, 1.8, 2, 6]].midiratio.plot;
|
||
|
a.plotMode = \levels; a.refresh;
|
||
|
a.superpose = false;
|
||
|
|
||
|
|
||
|
// Function:plot
|
||
|
a = { SinOsc.ar([700, 357]) * SinOsc.ar([400, 476]) * 0.2 }.plot;
|
||
|
a = { SinOsc.ar([700, 357] *0.02) * SinOsc.ar([400, 476]) * 0.3 }.plot(0.2, minval: -1);
|
||
|
a = { SinOsc.ar(440) }.plot(1);
|
||
|
|
||
|
|
||
|
// Env:plot
|
||
|
Env.perc(0.4, 0.6).plot;
|
||
|
Env.new({ 1.0.rand2 }! 8, { 1.0.rand } ! 7, \sin).plot;
|
||
|
|
||
|
// Buffer:plot
|
||
|
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/SinedPink.aiff");
|
||
|
// Platform.resourceDir +/+ "sounds/SinedPink.aiff" contains SinOsc on left, PinkNoise on right
|
||
|
b.plot;
|
||
|
b.free;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
|
||
|
|