Newer
Older
/*
* Simple Open Pixel Control client for Node.js
*
* 2013-2014 Micah Elizabeth Scott
* This file is released into the public domain.
*/
var net = require('net');
var OPC = function(host, port)
{
this.host = host;
this.port = port;
this.pixelBuffer = null;
};
OPC.prototype.loadModel = function(filename)
{
// Synchronously load a JSON model from a file on disk
return JSON.parse(fs.readFileSync(filename))
}
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
OPC.prototype._reconnect = function()
{
var _this = this;
this.socket = new net.Socket()
this.connected = false;
this.socket.onclose = function() {
console.log("Connection closed");
_this.socket = null;
_this.connected = false;
}
this.socket.connect(this.port, this.host, function() {
console.log("Connected to " + _this.socket.remoteAddress);
_this.connected = true;
_this.socket.setNoDelay();
});
}
OPC.prototype.writePixels = function()
{
if (!this.socket) {
this._reconnect();
}
if (!this.connected) {
return;
}
this.socket.write(this.pixelBuffer);
}
OPC.prototype.setPixelCount = function(num)
{
var length = 4 + num*3;
if (this.pixelBuffer == null || this.pixelBuffer.length != length) {
this.pixelBuffer = new Buffer(length);
}
// Initialize OPC header
this.pixelBuffer.writeUInt8(0, 0); // Channel
this.pixelBuffer.writeUInt8(0, 1); // Command
this.pixelBuffer.writeUInt16BE(num * 3, 2); // Length
}
OPC.prototype.setPixel = function(num, r, g, b)
{
var offset = 4 + num*3;
if (this.pixelBuffer == null || offset + 3 > this.pixelBuffer.length) {
this.setPixelCount(num + 1);
}
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, r | 0)), offset);
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, g | 0)), offset + 1);
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, b | 0)), offset + 2);
}
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
OPC.prototype.mapPixels = function(fn, model)
{
// Set all pixels, by mapping each element of "model" through "fn" and setting the
// corresponding pixel value. The function returns a tuple of three 8-bit RGB values.
// Implies 'writePixels' as well. Has no effect if the OPC client is disconnected.
if (!this.socket) {
this._reconnect();
}
if (!this.connected) {
return;
}
this.setPixelCount(model.length);
var offset = 4;
for (var i = 0; i < model.length; i++) {
var rgb = fn(model[i]);
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, rgb[0] | 0)), offset);
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, rgb[1] | 0)), offset + 1);
this.pixelBuffer.writeUInt8(Math.max(0, Math.min(255, rgb[2] | 0)), offset + 2);
offset += 3;
}
this.writePixels();
}
OPC.prototype.mapParticles = function(particles, model)
{
// Set all pixels, by mapping a particle system to each element of "model".
// The particles include parameters 'point', 'intensity', 'falloff', and 'color'.
function shader(p) {
var r = 0;
var g = 0;
var b = 0;
for (var i = 0; i < particles.length; i++) {
var particle = particles[i];
// Particle to sample distance
var dx = (p.point[0] - particle.point[0]) || 0;
var dy = (p.point[1] - particle.point[1]) || 0;
var dz = (p.point[2] - particle.point[2]) || 0;
var dist2 = dx * dx + dy * dy + dz * dz;
// Particle edge falloff
var intensity = particle.intensity / (1 + particle.falloff * dist2);
// Intensity scaling
r += particle.color[0] * intensity;
g += particle.color[1] * intensity;
b += particle.color[2] * intensity;
}
return [r, g, b];
}
this.mapPixels(shader, model);
}