File: NearestN.schelp

package info (click to toggle)
supercollider-sc3-plugins 3.7.1~repack-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 14,332 kB
  • ctags: 11,704
  • sloc: cpp: 140,180; lisp: 14,746; ansic: 2,133; xml: 86; makefile: 82; haskell: 21; sh: 8
file content (119 lines) | stat: -rw-r--r-- 3,684 bytes parent folder | download | duplicates (4)
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
)

}

)

::