node-red-contrib-easybotics-led-matrix
Version:
control led matrix with node-red on a raspberryPI, node-red binding of: https://www.npmjs.com/package/easybotics-rpi-rgb-led-matrix
1,143 lines (896 loc) • 34.6 kB
HTML
<script type="text/javascript">
function Point(x, y) {
this.x = parseInt(x);
this.y = parseInt(y);
}
function pixelReDraw (points, numPoints, pixelSelector, drawLines = true)
{
var canvas = document.getElementById(pixelSelector);
var context = canvas.getContext("2d");
context.beginPath();
for(i = 0; i < points.length; i++)
{
context.rect(points[i].x * 4+1, points[i].y * 4+1, 3, 3);
context.fillStyle = "#555";
context.fill();
}
context.closePath();
if(drawLines === true && points.length !== 0)
{
context.beginPath();
context.moveTo(points[0].x * 4 + 2.5, points[0].y * 4 + 2.5);
for(i = 1; i < points.length; i++)
{
context.lineTo(points[i].x * 4 + 2.5, points[i].y * 4 + 2.5);
}
if(points.length == numPoints) { //only close the shape if all points are there
context.lineTo(points[0].x * 4 + 2.5, points[0].y * 4 + 2.5);
}
context.lineWidth = 1;
context.strokeStyle = "#555";
context.stroke();
context.closePath();
}
}
function getMousePos(canvas, evt)
{
var rect = canvas.getBoundingClientRect();
return { //mouse x pos - how far rect is from edge of page / num of screen pixels per "pixel" in the canvas (finds what canvas pixel the mouse is in)
x: Math.floor((evt.clientX - rect.left) / 4),
y: Math.floor((evt.clientY - rect.top) / 4)
};
}
function drawGrid (canvas, context)
{
context.beginPath();
for (var x = 0.5; x < canvas.width; x += 4)
{
context.moveTo(x, 0);
context.lineTo(x, 256);
}
for (var y = 0.5; y < canvas.height; y += 4)
{
context.moveTo(0, y);
context.lineTo(512, y);
}
context.lineWidth = 1;
context.strokeStyle = "#ddd";
context.closePath();
context.stroke();
}
function ptNear(points, coord, range = 2) //coord: coordinate of point to check as object with format {x: x-coord, y: y-coord}, range: how far from the clicked pt to search for points
{
for(i = coord.x-range; i <= coord.x+range; i++) {
for(j = coord.y-range; j <= coord.y+range; j++) {
for(k = 0; k < points.length; k++) {
if(points[k].x === i && points[k].y === j) {
return k;
}
}
}
}
return false; //if no pts within range of clicked pt
}
/*
* function to draw a grid on the canvas for the pixel selector and give the coordinates of the selected pixel
* numPoints: number of points in the polygon (if string input, id of input field with number of pts),
* pixelSelector: canvas to use, drawLines: draw the resulting shape or not, updateFields: whether or not to change input text fields,
* colorSelector: id of the color selector to use for the shape, loadPts: array with points to draw on canvas
*/
function pixelSelector (numPoints, pixelSelector, drawLines = true, updateFields = true, colorSelector = false, loadPts = [])
{
var canvas = document.getElementById(pixelSelector);
var context = canvas.getContext("2d");
var currentPoint = {};
var points = loadPts;
var numPointsElement = numPoints;
if(typeof numPointsElement === "string") {
numPoints = parseInt(document.getElementById(numPointsElement).value);
}
if(colorSelector == false) { //if no color selector specified
var color = "#fff";
}
if(updateFields == true) { //get the numbers in the input fields
for(i = 0; i < numPoints; i++) {
points.push(new Point(document.getElementById("node-input-x"+i).value, document.getElementById("node-input-y"+i).value));
}
}
drawGrid(canvas, context);
pixelReDraw(points, numPoints, pixelSelector, drawLines);
canvas.addEventListener('mousedown', function(evt)
{
currentPoint.down = getMousePos(canvas, evt);
});
canvas.addEventListener('mouseup', function(evt)
{
currentPoint.up = getMousePos(canvas, evt);
if(typeof numPointsElement === "string") { //update the numPoints variable if it was changed from what it was as the edit dialog was opened
numPoints = parseInt(document.getElementById(numPointsElement).value);
}
if (ptNear(points, currentPoint.down) !== false) { //mouse dragged and starting on/near a point
//update the selected point
ptIndex = ptNear(points, currentPoint.down)
points[ptIndex] = currentPoint.up;
} else { //if mouse not starting on a point
points.push(new Point(currentPoint.up.x, currentPoint.up.y));
}
context.clearRect(0, 0, canvas.width, canvas.height);
drawGrid(canvas, context);
if(points.length > numPoints)
{ // if the number of points is too many, remove extra points
points.splice(0, points.length - numPoints);
}
for(i = 0; i < points.length; i++)
{
if(updateFields == true)
{
document.getElementById("node-input-x"+i).value = points[i].x; //set input fields to the coordinates of each pixel
document.getElementById("node-input-y"+i).value = points[i].y;
}
}
pixelReDraw(points, numPoints, pixelSelector, drawLines);
});
}
</script>
<script type="text/javascript">
RED.nodes.registerType('led-matrix', {
category: 'config',
defaults: {
height: {value:64, required:true},
width: {value:64, required:true},
chained: {value:2, required:true},
parallel: {value:1, required:true},
brightness: {value:100, required:true},
refreshDelay: {value:500, required:true},
mapping: {value:"adafruit-hat-pwm", required:true},
rgbSequence: {value: "RGB", required:true},
autoRefresh: {value:true},
cmdArgs: {value: ''},
},
oneditprepare: function ()
{
$("#node-config-refreshCheck").change( function()
{
const val = $("#node-config-refreshCheck").val();
this.autoRefresh = (val === "true");
});
if(this.autoRefresh)
{
$("#node-config-refreshCheck").val("true");
}
else
{
$("#node-config-refreshCheck").val("false");
}
$("#node-config-input-mapping").change(function()
{
this.mapping = $("#node-config-input-mapping").val();
});
$("#node-config-input-mapping").val(this.mapping);
},
oneditsave: function ()
{
},
label: function()
{
return "LED MATRIX";
},
});
</script>
<script type="text/x-red" data-template-name="led-matrix">
<div class="form-row">
<label for="node-config-input-width"><i class="icon-bookmark"></i> Width</label>
<input type="text" id="node-config-input-width">
<label for="node-config-input-height"><i class="icon-bookmark"></i> Height</label>
<input type="text" id="node-config-input-height">
</div>
<div class="form-row">
<label for="node-config-input-chained"><i class="icon-bookmark"></i> Chained Panels</label>
<input type="text" id="node-config-input-chained">
<label for="node-config-input-parallel"><i class="icon-bookmark"></i> Parallel Panels</label>
<input type="text" id="node-config-input-parallel">
</div>
<div class="form-row">
<label for="node-config-input-brightness"><i class="icon-bookmark"></i> Panel Brightness</label>
<input type="text" id="node-config-input-brightness">
</div>
<div class="form-row">
<label for="node-config-input-refreshDelay"> <i class= "icon-bookmark"></i> Refresh Delay milliseconds</label>
<input type="number" id="node-config-input-refreshDelay" min="0" placeholder="default: 500 milliseconds">
</div>
<div class="form-row">
<label for="node-config-input-mapping"><i class="icon-tag"></i> Hardware Mapping </label>
<select id= "node-config-input-mapping">
<option value="adafruit-hat-pwm"> Easybotics</option>
<option value="regular">Regular</option>
<option value="adafruit-hat">Adafruit Hat</option>
<option value="adafruit-hat-pwm">Adafruit Hat Pwm</option>
</select>
</div>
<div class="form-row">
<label for="node-config-input-rgbSequence"><i class="icon-tag"></i> RGB Sequence </label>
<input type="text" id="node-config-input-rgbSequence" placeholder="RGB">
</div>
<div class="form-row">
<label for="node-config-input-cmdArgs"><i class="icon-tag"></i>Command-Line Arguments</label>
<input type="text" id="node-config-input-cmdArgs">
</div>
<p>
<b> warning:</b> <br>
due to quirks with node-red, most of these settings won't apply <br>
unless you restart node-red (not the browser client),<br>
or delete this config node and make another one
</p>
</script>
<script type="text/x-red" data-help-name="led-matrix">
<p>
<br> <b> height: </b> height of the led panel
<br> <b> width: </b> width of the led panel, use the width of one panel if you are chaining them
<br> <b> chained panels: </b> amount of panels chained
<br> <b> parallel:</b> amount of panels wired in parallel
<br> <b> brightness:</b> brightness as a percentage, 1 - 100
<br> <b> mapping: </b> type of hardware mapping, what pins hzeller will output too
<br> <b> rgb sequence: </b> the letters "R", "G", and "B", arranged according to your display's rgb sequence
<br> <b> autoRefresh: </b> if true flow will try and keep the display updated, otherwise use the refresh node manually
<br> <b> refresh delay: </b> when autorefresh is turned on, the flow will never update the screen faster than this delay
<br> <b> Command Line arguments: </b> enter cli arguments here, example '--led-rgb-sequence=GRB'. Doesn't support runtime parameters so no messing with privilege dropping or daemon-modes. Seperate with spaces as in the cli proper
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType("refresh-matrix",
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value:""},
matrix: {type: 'led-matrix', required:true},
},
inputs:1,
outputs:0,
icon: "refresh.png",
label: function ()
{
return this.name || "refresh-matrix";
}
});
</script>
<script type="text/x-red" data-template-name="refresh-matrix">
<div class="form-row">
<label for="node-config-input-matrix"><i class='fa fa-tag'></i> Display </label>
<input type="text" id="node-input-matrix"/>
</div>
<div class="form-row">
<label for="node-config-input-name"> <i class="icon-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>
<script type="text/x-red" data-help-name="refresh-matrix">
<p> updates the display with vysnc <br><br>
nodes usually modify a in-memory buffer rather than pushing to the screen frame buffer itself, this node pushes the in memory buffer to the frame buffer when injected
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('image-to-matrix',
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value:""},
file: {value:""},
xOffset: {value:0, required:true},
yOffset: {value:0, required:true},
matrix: { type:'led-matrix', required:true},
zLevel: {value: undefined},
},
inputs:1,
outputs:0,
icon: "image.png",
label: function()
{
return this.name || "image";
}
});
</script>
<script type="text/x-red" data-template-name="image-to-matrix">
<div class='form-row'>
<label for="node-config-input-matrix"><i class='fa fa-tag'></i> Display</label>
<input type='text' id='node-input-matrix'/>
</div>
<div class="form-row">
<label for="node-config-input-name"> <i class="icon-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-config-input-file"> <i class="icon-tag"></i> File </label>
<input type="text" id="node-input-file" placeholder="File">
</div>
<div class="form-row">
<label for="node-config-input-xOffset"> <i class="fa fa-tag"></i> X position</label>
<input type="number" id="node-input-xOffset" min="0" placeholder="0"/>
<div>
<div class="form-row">
<label for="node-config-input-yOffset"> <i class="fa fa-tag"></i> Y position</label>
<input type="number" id="node-input-yOffset" min="0" placeholder="0" />
<div>
<div class="form-row">
<label for="node-config-input-zLevel"><i class="icon-tag"></i> z level</label>
<input type="number" id="node-input-zLevel" placeholder="-">
</div>
</script>
<script type="text/x-red" data-help-name="image-to-matrix">
<p> Takes a url, or a file path as a payload, and uses the x,y in the settings <br>
If a gif is injected, the frame displayed will advance on each inject <br><br>
Also takes an object with the following properties: <br>
<b>x:</b> x location<br>
<b>y:</b>y location<br>
<b>data</b>: the url or file path to a valid image <br>
<br> <b>object example: </b> <br>
<i>
msg.payload = { <br>
  x: 10, <br>
  y: 10, <br>
  data: "https://media.giphy.com/media/UaoxTrl8z1wre/giphy.gif" <br>
}; <br>
</i>
</p>
</script>
<script type="text/javascript">
function componentToHex(c)
{
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b)
{
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
RED.nodes.registerType('text-to-matrix',{
category: 'LED Matrix',
color: '#a6bbcf',
defaults: {
name: {value:""},
xOffset: {value:0, required: true},
yOffset: {value:0, required: true},
rgb: {value:"0,255,41"},
font: {value:"9x18B.bdf", required:true},
prefix: {value:""},
source: {value:"msg.payload"},
matrix: { type:'led-matrix', required:true},
zLevel: {value: undefined},
},
inputs:1,
outputs:0,
icon: "text.png",
label: function() {
return this.name ||"text";
},
oneditprepare: function ()
{
const fontDropdown = $("#node-config-input-font");
// fontDropdown.append('<option value="_msg_">- set from msg.deviceid -</option>');
const fonts = ["10x20.bdf", "5x8.bdf", "6x13B.bdf", "6x9.bdf", "7x13O.bdf", "8x13B.bdf", "9x15B.bdf", "9x18.bdf", "helvR12.bdf", "tom-thumb.bdf",
"4x6.bdf", "6x10.bdf", "6x13.bdf", "7x13B.bdf", "7x14B.bdf", "8x13.bdf", "9x15.bdf", "5x7.bdf", "6x12.bdf", "6x13O.bdf", "7x13.bdf", "7x14.bdf", "8x13O.bdf", "9x18B.bdf", "clR6x12.bdf"]
fonts.sort();
for(var i = 0; i < fonts.length; i++)
{
const f = fonts[i];
const text = f;
const value = f;
//fontDropdown.append('<option value="_msg_">- set from msg.deviceid -</option>');
fontDropdown.append('<option value="' + value + '"> ' + text + '</option>');
console.log('<option value="' + value + '">- ' + text + ' -</option>');
};
fontDropdown.change(function()
{
this.font = fontDropdown.val();
});
if(this.font) fontDropdown.val(this.font);
else fontDropdown.val("5x8.bdf");
$("#node-input-source").change(function()
{
this.source = $("#node-input-source").val();
});
$("#node-input-source").val(this.source || "msg.payload");
$("#node-input-rgb2").change(function()
{
var hex = $("#node-input-rgb2").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgb").val(output);
});
var tr = $("#node-input-rgb").val();
var split = tr.split(',');
$("#node-input-rgb2").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
},
oneditsave: function ()
{
this.font = $("#node-config-input-font").val();
//this.font = $("#node-config-input-font").val();
},
});
</script>
<script type="text/x-red" data-template-name="text-to-matrix">
<div class='form-row'>
<label for="node-config-input-matrix"><i class='fa fa-tag'></i> Display</label>
<input type='text' id='node-input-matrix'/>
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class='form-row' id="node-config-input-front-row">
<label for='node-config-input-font'><i class='icon-tag'></i> Font</label>
<select id="node-config-input-font">
</select>
</div>
<div class="form-row">
<label for="node-config-input-xOffset"> <i class="fa fa-tag"></i> X position</label>
<input type="number" id="node-input-xOffset" min="0" placeholder="0"/>
<label for="node-config-input-yOffset"> <i class="fa fa-tag"></i> Y position</label>
<input type="number" id="node-input-yOffset" min="0" placeholder="0" />
</div>
<div class= "form-row">
<label for="node-config-input-prefix"> <i class="fa fa-tag"></i> Prefix text</label>
<input type="text" id="node-input-prefix" />
<label for="node-config-input-source"> <i class="fa fa-tag"></i> Source</label>
<input type="text" id="node-input-source" />
</div>
<div class = "form-row">
<label for="node-config-input-rgb"><i class="icon-tag"></i>RGB color </label>
<input type="text" id="node-input-rgb" placeholder="112,79,86">
<label for="node-config-input-rgb2"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2" placeholder="#43DB2D">
</div>
<div class="form-row">
<label for="node-config-input-zLevel"><i class="icon-tag"></i> z level</label>
<input type="number" id="node-input-zLevel" placeholder="-">
</div>
</script>
<script type="text/x-red" data-help-name="text-to-matrix">
<p>prints text to the matrix buffer <br>
If <i> source </i> is a string it uses the x and y coordinates from the settings. <br>
<i> source </i> can also be an object with the following properties: <br>
<b> x:</b> X location <br>
<b> y:</b> Y location <br>
<b> rgb:</b> rgb color, as a CSV string. Example: "255,0,0" (RED) <br>
<b> data:</b> text to print <br><br>
The prefix setting prefixes a string to the output text<br> <br>
<b> object example: </b> <br>
<i>
msg.payload = { <br>
  x: 10, <br>
  y: 10, <br>
  data: "hello world", <br>
  rgb: "255,0,0" <br>
}; <br>
<br<br> <b> warning: source is evalulated with eval() </b>
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('pixel-transform',
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value:""},
xOffset: {value:0},
yOffset: {value:0},
refresh: {value:0},
rgb: {value:"0,255,41"},
},
inputs: 1,
outputs: 1,
icon: "arrow-in.png",
label: function()
{
return this.name || "Data Transform!";
},
oneditprepare: function ()
{
$("#node-input-rgb2").change(function()
{
var hex = $("#node-input-rgb2").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgb").val(output);
});
var tr = $("#node-input-rgb").val();
var split = tr.split(',');
$("#node-input-rgb2").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
},
oneditsave: function ()
{
},
});
</script>
<script type="text/x-red" data-template-name="pixel-transform">
<div class = "form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class = "form-row">
<label for="node-config-input-xOffset"><i class="icon-tag"></i>X </label>
<input type="text" id="node-input-xOffset" placeholder="0">
</div>
<div class = "form-row">
<label for="node-config-input-yOffset"><i class="icon-tag"></i>Y </label>
<input type="text" id="node-input-yOffset" placeholder="0">
</div>
<div class = "form-row">
<label for="node-config-input-rgb"><i class="icon-tag"></i>RGB color </label>
<input type="text" id="node-input-rgb" placeholder="255,255,255">
</div>
<div class = "form-row">
<label for="node-config-input-rgb2"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2">
</div>
</script>
<script type="text/x-red" data-help-name="pixel-transform">
<p>modifies properties of a display object, or spawns one from a string <br> <br>
if input is a string will output an object with the following properties: <br><br>
<b> data: </b> original string input <br>
<b> x:</b> X location <br>
<b> y:</b> Y location <br>
<b> rgb:</b> rgb color, as a CSV string. Example: "255,0,0" (RED) <br><br>
if input is an object, those properties will be modified
</p>
</script>
<script type="text/javascript">
function componentToHex(c)
{
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b)
{
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
RED.nodes.registerType('circle',
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value:""},
xPos: {value:0, required: true},
yPos: {value:0, required: true},
radius: {value:0, required: true},
rgb: {value:"0,255,41"},
matrix: {type: 'led-matrix', required:true},
zLevel: {value: undefined},
},
inputs: 1,
outputs:0,
icon: "circle.png",
label: function ()
{
return this.name || "circle";
},
oneditprepare: function ()
{
$("#node-input-rgb2").change(function()
{
var hex = $("#node-input-rgb2").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgb").val(output);
});
var tr = $("#node-input-rgb").val();
var split = tr.split(',');
$("#node-input-rgb2").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
},
});
</script>
<script type="text/x-red" data-template-name="circle">
<div class='form-row'>
<label for="node-config-input-matrix"><i class='fa fa-tag'></i> Display</label>
<input type='text' id='node-input-matrix'/>
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-config-input-xPos"> <i class="fa fa-tag"></i> X Position </label>
<input type="number" id="node-input-xPos" min = "0" placeholder="0" />
<div>
<div class="form-row">
<label for="node-config-input-yPos"> <i class="fa fa-tag"></i> Y Position </label>
<input type="number" id="node-input-yPos" min = "0" placeholder="0" />
<div>
<div class="form-row">
<label for="node-config-input-radius"> <i class="fa fa-tag"></i> Radius </label>
<input type="number" id="node-input-radius" min = "0" placeholder="0" />
<div>
<div class = "form-row">
<label for="node-config-input-rgb"><i class="icon-tag"></i>RGB color </label>
<input type="text" id="node-input-rgb" placeholder="112,79,86">
</div>
<div class = "form-row">
<label for="node-config-input-rgb2"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2" placeholder="#43DB2D">
</div>
<div class="form-row">
<label for="node-config-input-zLevel"><i class="icon-tag"></i> z level</label>
<input type="number" id="node-input-zLevel" placeholder="-">
</div>
</script>
<script type="text/x-red" data-help-name="circle">
<p> Prints a circle to the matrix buffer <br>
<i> msg.payload </i> can be an object with the following properties: <br>
<b> x: </b> X location <br>
<b> y: </b> Y location <br>
<b> radius: </b> radius <br>
<b> rgb: </b> an rgb CSV string, example : "255,0,0," (red) <br><br>
otherwise the settings from the node itself are used </p>
<p>
<br> <b>object example: </b> <br>
<i>
msg.payload = { <br>
  x: 10, <br>
  y: 10, <br>
  radius: 5, <br>
  rgb: "255,255,0" <br>
}; <br>
</i>;
</p>
</script>
<script type="text/javascript">
function componentToHex(c)
{
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b)
{
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
RED.nodes.registerType('polygon',
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value:""},
rgb: {value: "255,255,255"},
matrix: {type: 'led-matrix', required: true},
numPts: {value: "2"},
zLevel: {value: undefined},
savedPts: {value: []},
tempPts: {value: []},
xOffset: {value: 0, required: true},
yOffset: {value: 0, required: true},
allowDeploy:{value: 0}, //see comment in oneditsave
filled: {value: false},
},
inputs: 1,
outputs: 0,
icon: "polygon.png",
label: function ()
{
return this.name || "polygon";
},
oneditprepare: function ()
{
$("#node-input-rgb2").change(function()
{
var hex = $("#node-input-rgb2").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgb").val(output);
});
var tr = $("#node-input-rgb").val();
var split = tr.split(',');
$("#node-input-rgb2").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
this.tempPts = this.savedPts.slice(0); //create a copy of savedPts in case edits are made in edit dialog but not saved
pixelSelector("node-input-numPts", "canvasPixelSelect", true, false, "node-input-rgb2", this.savedPts);
},
oneditcancel: function ()
{
//this.savedPts is directly edited in the pixelSelector function, so if the edit dialog is cancelled, it needs to be reset
this.savedPts = this.tempPts.slice(0);
},
oneditsave: function ()
{
//node red only allows the user to press the deploy button if something in an html input changes
//the object with points isn't tied to any html input, so this is needed to let the user click deploy
if(JSON.stringify(this.savedPts) !== JSON.stringify(this.tempPts)) { //if changes are made to the points
this.allowDeploy++; //update the hidden input "allowDeploy"
}
},
});
</script>
<script type="text/x-red" data-template-name="polygon">
<div class='form-row'>
<label for="node-config-input-matrix"><i class='fa fa-tag'></i>Display</label>
<input type='text' id='node-input-matrix'>
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i>Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class = "form-row">
<label for="node-config-input-rgb"><i class="icon-tag"></i>RGB color</label>
<input type="text" id="node-input-rgb" placeholder="255,255,255">
</div>
<div class = "form-row">
<label for="node-config-input-rgb2"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2">
</div>
<div class='form-row'>
<label for="node-config-input-numPts"><i class='fa fa-tag'></i>Number of points</label>
<input type='number' min='1' step='1' id='node-input-numPts'/>
</div>
<div class='form-row'>
<label for="node-config-input-xOffset"><i class='fa fa-tag'></i>X Offset</label>
<input type='number' min='0' placeholder='0' step='1' id='node-input-xOffset'/>
</div>
<div class='form-row'>
<label for="node-config-input-yOffset"><i class='fa fa-tag'></i>Y Offset</label>
<input type='number' min='0' placeholder='0' step='1' id='node-input-yOffset'/>
</div>
<div class='form-row'>
<label for="node-config-input-filled"><i class='fa fa-tag'></i>Filled</label>
<input type='checkbox' id='node-input-filled' style='width: auto;'/>
</div>
<canvas id="canvasPixelSelect" style="outline: 1px solid black;" width="512" height="256"></canvas>
<input type="number" id="node-input-allowDeploy" style="display: none">
<div class="form-row">
<label for="node-config-input-zLevel"><i class="icon-tag"></i>z level</label>
<input type="number" id="node-input-zLevel" placeholder="-">
</div>
</script>
<script type="text/x-red" data-help-name="polygon">
<p> Prints a polygon to the matrix buffer, refresh the matrix to see the result<br>
When injected it will add itself to the buffer, and will continue to be printed until a msg.clear property is injected into it<br>
Will use the location and color from the settings, or can be injected with an object with the following properties:<br><br>
<b> rgb:</b> rgb color, as a CSV string. Example: "255,0,0" (RED)<br>
<b> filled:</b> boolean determining if the shape will be filled<br>
<b> savedPts:</b> what points to draw the polygon with. Use the following format, replacing x1, x2, y1, y2, etc. with the numerical coordinates of the points:<br>
<code>
[{x: x1, y: y1}, {x: x2, y: y2}]
</code>
<br><br> <b>object example:</b> <br>
<i>
msg.payload = {<br>
  savedPts: [{x:10, y:10}, {x:25, y:10}, {x:25, y:25}, {x:10, y:25}],<br>
  rgb: "255,255,0",<br>
  filled: false<br>
};<br>
</i>
</p>
</script>
<script type="text/javascript">
function componentToHex(c)
{
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b)
{
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
RED.nodes.registerType('graph-to-matrix',
{
category: 'LED Matrix',
color: '#a6bbcf',
defaults:
{
name: {value: ""},
rgb: {value: "255, 255, 255"},
rgbTick: {value: "255, 0, 0"},
xOffset: {value:0, required: true},
yOffset: {value:0, required: true},
height: {value:0, required: true},
width: {value:0, required: true},
minIn: {value: 0, required: true},
maxIn: {value: 0, required: true},
matrix: {type: 'led-matrix', required: true},
},
inputs: 1,
outputs: 0,
icon: "circle.png",
label: function ()
{
return this.name || "graph";
},
oneditprepare: function ()
{
$("#node-input-rgb2").change(function()
{
var hex = $("#node-input-rgb2").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgb").val(output);
});
var tr = $("#node-input-rgb").val();
var split = tr.split(',');
$("#node-input-rgb2").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
$("#node-input-rgb2Tick").change(function()
{
var hex = $("#node-input-rgb2Tick").val();
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var output = "" + parseInt(result[1], 16) + ',' + parseInt(result[2], 16) + ',' + parseInt(result[3], 16);
$("#node-input-rgbTick").val(output);
});
var tr = $("#node-input-rgbTick").val();
var split = tr.split(',');
$("#node-input-rgb2Tick").val( rgbToHex( parseInt(split[0]), parseInt(split[1]), parseInt(split[2])));
}
});
</script>
<script type="text/x-red" data-template-name="graph-to-matrix">
<div class="form-row">
<label for="node-config-input-matrix"><i class='fa fa-tag'></i> Display</label>
<input type='text' id='node-input-matrix'/>
</div>
<div class="form-row">
<label for="node-config-input-name"><i class="icon-tag"></i> Name </label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class = "form-row">
<label for="node-config-input-rgb"><i class="icon-tag"></i>Line RGB color </label>
<input type="text" id="node-input-rgb" placeholder="112,79,86">
<label for="node-config-input-rgb2"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2" placeholder="#43DB2D">
</div>
<div class = "form-row">
<label for="node-config-input-rgbTick"><i class="icon-tag"></i>Tick RGB color </label>
<input type="text" id="node-input-rgbTick" placeholder="112,79,86">
<label for="node-config-input-rgb2Tick"><i class="icon-tag"></i>Color picker</label>
<input type="color" id="node-input-rgb2Tick" placeholder="#43DB2D">
</div>
<div class="form-row">
<label for="node-config-input-xOffset"> <i class="fa fa-tag"></i> X position</label>
<input type="number" id="node-input-xOffset" min="0" placeholder="0"/>
<label for="node-config-input-yOffset"> <i class="fa fa-tag"></i> Y position</label>
<input type="number" id="node-input-yOffset" min="0" placeholder="0" />
</div>
<div class="form-row">
<label for="node-config-input-height"> <i class="fa fa-tag"></i> Height</label>
<input type="number" id="node-input-height" min="0" placeholder="0"/>
<label for="node-config-input-width"> <i class="fa fa-tag"></i> Width</label>
<input type="number" id="node-input-width" min="0" placeholder="0" />
</div>
<div class="form-row">
<label for="node-config-input-minIn"> <i class="fa fa-tag"></i> min input</label>
<input type="number" id="node-input-minIn" min="0" placeholder="0"/>
<label for="node-config-input-maxIn"> <i class="fa fa-tag"></i> max input</label>
<input type="number" id="node-input-maxIn" min="0" placeholder="0" />
</div>
</script>
<script type="text/x-red" data-help-name="graph-to-matrix">
<p> Takes a number as the payload and uses it to print a graph <br>
Draws one dot per input, use the delay node to rate-limit faster inputs <br> <br>
The x and y options are for the top left origin of the graph <br>
Min and Max input are what values the top and the bottom of the graph will scale too <br>
Line color is the color of the line <br>
Tick color is changes the color of the most recent data point <br>
</script>