1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
CLASS:: NearestN
summary:: Find the nearest-neighbours in a set of points
categories:: UGens>Analysis
related:: Classes/KDTree
DESCRIPTION::
This UGen takes an input point (such as a 2D or 3D location) and searches for the nearest few points in a dataset (by Euclidean distance).
strong::You need the KDTree quark to generate the data for this UGen.::
CLASSMETHODS::
METHOD:: kr
argument:: treebuf
a Buffer containing data which MUST be in the special format created using the KDTree class and NearestN.makeBufferData
argument:: in
An array representing the input point. Num channels must match the dimensionality of the points in the dataset
argument:: gate
The unit is active while gate > 0. While <=0, no search is performed and output is held steady
argument:: num
Number of points to retrieve. NOT modulatable.
returns:: A flat array containing (3 * num) channels, which is a ranked list of the nearest items (the very nearest coming first). For each match, this returns three channels [treeindex, distancesquared, label]
METHOD:: makeBufferData
Processes a KDTree into a special format that can be loaded into a Buffer, and then searched by this UGen.
EXAMPLES::
The most basic workflow is something like this:
code::
// Find/make a dataset of points:
x = 15.collect{[1.0.rand, 1.0.rand]};
// Append the index onto the end, for later reference
x = x.collect{|data, i| data ++ i };
// Use the KDTree quark and the .makeBufferData method to put the data in the searchable format
~treedata = NearestN.makeBufferData(KDTree(x, lastIsLabel: true));
// Load the data to the server
~treebuf = Buffer.sendCollection(s, ~treedata.flat, 5);
// Run a synth with the ugen and some kind of query data
~synth = { NearestN.kr(~treebuf, {LFNoise0.kr(1)}.dup, num:3).poll(1)}.play;
~synth.free
::
This more complete example shows it in action:
code::
(
s.waitForBoot{
(
x = 15.collect{|i| [1.0.rand, 1.0.rand, i]};
~treedata = NearestN.makeBufferData(KDTree(x, lastIsLabel: true));
~size = 800;
~treebuf = Buffer.sendCollection(s, ~treedata.flat, 5);
~foundbus = Bus.control(s, 3);
~mousebus = Bus.control(s, 2);
// lang-side cache of values which we'll poll from the server:
~mousepos = [0.5,0.5];
~nearests = [0,1,2];
);
(
w = Window.new("points of proximity", Rect(100, 100, ~size, ~size)).front;
w.drawHook = {
var thesize, y;
Pen.color = Color.white;
Pen.fillRect(Rect(0, 0, ~size, ~size));
x.do{|ax, index|
y = 1 - ax[1]; // y is flipped for drawing
Pen.color = Color.blue.alpha_(0.6);
switch(~nearests.indexOf(index),
0, { Pen.fillOval(Rect(ax[0] * ~size - 10, y * ~size - 10, 20, 20)) },
1, { Pen.fillOval(Rect(ax[0] * ~size - 8, y * ~size - 8, 16, 16)) },
2, { Pen.fillOval(Rect(ax[0] * ~size - 5, y * ~size - 5, 10, 10)) }
);
Pen.color = Color.black;
Pen.fillOval(Rect(ax[0] * ~size - 2, y * ~size - 2, 4, 4));
Pen.stringAtPoint(index.asString, ax[0] @ y * ~size, Font.default, Color.black);
};
Pen.color = Color.green.alpha_(0.5);
Pen.fillOval(Rect(~mousepos[0] * ~size - 7, (1.0-~mousepos[1]) * ~size - 7, 14, 14));
};
);
s.sync;
(
~synth = {
var mouse = Latch.kr([MouseX.kr, MouseY.kr], Impulse.kr(10));
var found = NearestN.kr(~treebuf, mouse, num:3);
//found.poll(1);
var findices = found[2,5..]; // original array index, stored as 'labels'
var fdistsq = found[1,4..]; // distances
Out.kr(~foundbus, findices);
Out.kr(~mousebus, mouse);
Out.ar(0, SinOsc.ar((findices * 2 + 60).midicps, 0, fdistsq.explin(1e-6, 1, 0.5, 0)).sum);
}.play;
~updater = Task{loop{
~foundbus.getn(3, {|data| ~nearests = data.asInt});
~mousebus.getn(2, {|data| ~mousepos = data});
s.sync;
defer{w.refresh};
0.1.wait;
}}.play
)
}
)
::
|