Skip to content

Commit

Permalink
added a graph node, rendering with the polygon DP class
Browse files Browse the repository at this point in the history
  • Loading branch information
keptan committed Jul 26, 2019
1 parent d6ac6b8 commit 856c3ca
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 4 deletions.
154 changes: 154 additions & 0 deletions pixel.html
Expand Up @@ -986,3 +986,157 @@
</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>


102 changes: 98 additions & 4 deletions pixel.js
Expand Up @@ -336,8 +336,8 @@ module.exports = function(RED) {
RED.nodes.createNode(this, config)
var node = this

node.matrix = RED.nodes.getNode(config.matrix)
node.prefix = config.prefix || ''
node.matrix = RED.nodes.getNode(config.matrix) //fetch the matrix instantiation from the config
node.prefix = config.prefix || ''
node.source = config.source || 'msg.payload'
node.font = config.font
node.xOffset = parse.validateOrDefault(config.xOffset, 0)
Expand All @@ -351,12 +351,14 @@ module.exports = function(RED) {
{
if(outputInfo != undefined)
{
//when drawing we calculate the fonr directory, and call the drawText
const color = new dp.Color().fromRgbString(outputInfo.rgb)
const fontDir = __dirname + '/fonts/' + node.font
led.drawText(parseInt(outputInfo.x), parseInt(outputInfo.y), outputInfo.data, fontDir, parseInt(color.r), parseInt(color.g), parseInt(color.b))
}
}

//dont draw this node anymore on the matrix
node.clear = function ()
{
nodeRegister.delete(node)
Expand All @@ -371,8 +373,12 @@ module.exports = function(RED) {
return
}

//scary and dangerous!
//RCE waiting to happen
const outputData = eval( node.source)


//round floats
const handleFloat = function (i)
{
if( !isNaN(i))
Expand All @@ -383,6 +389,7 @@ module.exports = function(RED) {
return i
}

//handle being handed a struct instead of a string
if(outputData != undefined)
{

Expand Down Expand Up @@ -514,6 +521,9 @@ module.exports = function(RED) {
})
}

/*draws a bounded polygon to the display, either filled or not filled
* can be designed in the settings using a little drawing tool
*/
function Polygon (config)
{
RED.nodes.createNode(this, config)
Expand All @@ -523,8 +533,7 @@ module.exports = function(RED) {
//get the config data we'll use later
node.zLevel = parse.validateOrDefault(config.zLevel, 1)
node.savedPts = config.savedPts
node.offset = new dp.Point(parse.validateOrDefault(config.xOffset, 0),
parse.validateOrDefault(config.yOffset, 0))
node.offset = new dp.Point(parse.validateOrDefault(config.xOffset, 0), parse.validateOrDefault(config.yOffset, 0))
node.rgb = config.rgb || '255,255,255'
node.filled = config.filled || false

Expand Down Expand Up @@ -588,6 +597,7 @@ module.exports = function(RED) {
var runColor = undefined
var runFilled = undefined

//if we aren't handed new data we'll use the one from last time
if(data.savedPts) runPts = data.savedPts
if(data.filled) runFilled = data.filled
if(data.rgb) runColor = data.rgb
Expand All @@ -601,6 +611,7 @@ module.exports = function(RED) {
node.color = new dp.Color().fromRgbString(runColor)


//can we use the cached polygon?
if(node.polygon && (node.oldPoints == runPts && node.oldFilled == runFilled))
return

Expand Down Expand Up @@ -630,9 +641,92 @@ module.exports = function(RED) {

}

/*draws a scaled line graph in a boxed area*/
function Graph (config)
{
RED.nodes.createNode(this, config)
const node = this
node.matrix = RED.nodes.getNode(config.matrix)

//get the config data we'll use later
node.zLevel = -10

//these are the values we'll scale the graph between
//for example, with the default values here
//100 would be the very bottom of the graph
//and 1500 would be the very top of the graph
node.inMin = parse.validateOrDefault(config.minIn, 100)
node.inMax = parse.validateOrDefault(config.maxIn, 1500)

//width and height of the graph, origin is the top left corner
node.width = parse.validateOrDefault(config.width, 100)
node.height = parse.validateOrDefault(config.height, 50)

//origin point
node.x = parse.validateOrDefault(config.xOffset, 0)
node.y = parse.validateOrDefault(config.yOffset, 0)

//how far along the graph we're in
node.tick = 0

node.rgb = parse.validateOrDefault(config.rgb, '255,255,255')
node.rgbTick = parse.validateOrDefault(config.rgbTick, '255, 0, 0')

node.color = new dp.Color().fromRgbString(node.rgb)
node.bg = new dp.Color().fromRgbString(node.rgbTick)

node.data = []
node.polygon = undefined
node.raw = 0

//normalizer
node.scale = function (data, inMin, inMax, outMin, outMax)
{
return ( ((data - inMin) / (inMax - inMin)) * (outMax - outMin)) + outMin
}

node.on('input', function (msg)
{
if(msg.clear)
{
node.clear()
return
}

const data = node.scale(msg.payload, node.inMin, node.inMax, node.height, 0)
node.raw = data
node.data[node.tick % node.width] = {x: (node.tick % node.width), y: data}

node.polygon = new dp.Polygon([].concat.apply([],[ node.data , node.data.slice().reverse()]))

node.tick++

readySend()
})

node.draw = function ()
{
if(node.polygon)
{
node.polygon.draw(led, node.color, new dp.Point(node.x, node.y))
dot = new dp.Line(new dp.Point((node.tick - 1) % node.width, node.raw - 1), new dp.Point((node.tick - 1) % node.width, node.raw + 1))

dot.draw(led, node.bg, new dp.Point(node.x, node.y))
}
}

function readySend()
{
nodeRegister.add(node)
node.matrix.refresh()
}
}


//register our functions with node-red
RED.nodes.registerType('led-matrix', LedMatrix)
//RED.nodes.registerType('clear-matrix', ClearMatrix)
RED.nodes.registerType('graph-to-matrix', Graph)
RED.nodes.registerType('refresh-matrix', RefreshMatrix)
RED.nodes.registerType('image-to-matrix', ImageToPixels)
RED.nodes.registerType('text-to-matrix', Text)
Expand Down

0 comments on commit 856c3ca

Please sign in to comment.