UNPKG

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
<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> &emsp;&emsp;x: 10, <br> &emsp;&emsp;y: 10, <br> &emsp;&emsp;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> &emsp;&emsp;x: 10, <br> &emsp;&emsp;y: 10, <br> &emsp;&emsp;data: "hello world", <br> &emsp;&emsp;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> &emsp;&emsp;x: 10, <br> &emsp;&emsp;y: 10, <br> &emsp;&emsp;radius: 5, <br> &emsp;&emsp;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> &emsp;&emsp;savedPts: [{x:10, y:10}, {x:25, y:10}, {x:25, y:25}, {x:10, y:25}],<br> &emsp;&emsp;rgb: "255,255,0",<br> &emsp;&emsp;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>