rpd
Version:
RPD is a minimal framework for building Node-Based User Interfaces, powered by Reactive Programming
176 lines (157 loc) • 7.4 kB
JavaScript
(function() {
var SHAPES = [ 'circle', 'rect', 'cross', 'diamond' ];
var DEFAULT_COLOR = { r: 0xED, g: 0x22, b: 0x5D };
var SVG_XMLNS = "http://www.w3.org/2000/svg";
// ============= Register p5/color channel type =============
function numberToHex(num) { return (num > 15) ? num.toString(16) : '0' + num.toString(16); }
function toHexColor(color) {
return '#' + numberToHex(color.r || 0)
+ numberToHex(color.g || 0)
+ numberToHex(color.b || 0);
}
Rpd.channeltype('p5/color', { show: toHexColor });
// ============= Register p5/color node type and renderer =============
Rpd.nodetype('p5/color', {
inlets: {
'r': { type: 'util/number', default: DEFAULT_COLOR.r, name: 'red' },
'g': { type: 'util/number', default: DEFAULT_COLOR.g, name: 'green' },
'b': { type: 'util/number', default: DEFAULT_COLOR.b, name: 'blue' }
},
outlets: {
'color': { type: 'p5/color' }
},
process: function(inlets) { return { color: inlets }; }
});
Rpd.noderenderer('p5/color', 'svg', function() {
var colorElm, textElm;
return {
size: { width: 90, height: 40 },
first: function(bodyElm) {
var group = document.createElementNS(SVG_XMLNS, 'g');
group.setAttributeNS(null, 'transform', 'translate(-30, 0)');
colorElm = document.createElementNS(SVG_XMLNS, 'rect');
colorElm.setAttributeNS(null, 'y', -10);
colorElm.setAttributeNS(null, 'width', 20);
colorElm.setAttributeNS(null, 'height', 20);
colorElm.setAttributeNS(null, 'rx', 2);
colorElm.setAttributeNS(null, 'ry', 2);
group.appendChild(colorElm);
textElm = document.createElementNS(SVG_XMLNS, 'text');
textElm.style.alignmentBaseline = 'middle';
textElm.setAttributeNS(null, 'x', 25);
textElm.setAttributeNS(null, 'y', 1);
textElm.innerText = textElm.textContent = '<None>';
group.appendChild(textElm);
bodyElm.appendChild(group);
},
always: function(bodyElm, inlets, outlets) {
var color = outlets.color;
colorElm.style.fill = 'rgb(' + color.r + ',' + color.g + ',' + color.b + ')';
textElm.innerText = textElm.textContent = toHexColor(color);
}
};
});
// ============= Register p5/shape channel type =============
Rpd.channeltype('p5/shape', {});
// ============= Register p5/shape node type and renderer =============
Rpd.nodetype('p5/shape', {
inlets: { 'shape': { type: 'p5/shape', default: 'circle', hidden: true } },
outlets: { 'shape': { type: 'p5/shape' } },
process: function(inlets) { return { shape: inlets.shape } }
});
Rpd.noderenderer('p5/shape', 'svg', function() {
var curShape = 'circle';
var variants = {};
var symbolSide = 10;
var symbols = {
'circle': function() {
var circle = document.createElementNS(SVG_XMLNS, 'circle');
circle.setAttributeNS(null, 'r', Math.floor(symbolSide / 2));
return circle;
},
'rect': function() {
var rect = document.createElementNS(SVG_XMLNS, 'rect');
rect.setAttributeNS(null, 'width', symbolSide);
rect.setAttributeNS(null, 'height', symbolSide);
rect.setAttributeNS(null, 'x', -1 * Math.floor(symbolSide / 2));
rect.setAttributeNS(null, 'y', -1 * Math.floor(symbolSide / 2));
return rect;
},
'cross': function() {
var group = document.createElementNS(SVG_XMLNS, 'g');
group.setAttributeNS(null, 'transform', 'translate(' + (-1 * Math.floor(symbolSide / 2)) + ', '
+ (-1 * Math.floor(symbolSide / 2)) + ')');
var line1 = document.createElementNS(SVG_XMLNS, 'line');
line1.setAttributeNS(null, 'x1', 0);
line1.setAttributeNS(null, 'y1', 0);
line1.setAttributeNS(null, 'x2', Math.floor(symbolSide));
line1.setAttributeNS(null, 'y2', Math.floor(symbolSide));
group.appendChild(line1);
var line2 = document.createElementNS(SVG_XMLNS, 'line');
line2.setAttributeNS(null, 'x1', Math.floor(symbolSide));
line2.setAttributeNS(null, 'y1', 0);
line2.setAttributeNS(null, 'x2', 0);
line2.setAttributeNS(null, 'y2', Math.floor(symbolSide));
group.appendChild(line2);
return group;
},
'diamond': function() {
var rect = document.createElementNS(SVG_XMLNS, 'rect');
rect.setAttributeNS(null, 'transform', 'rotate(45)');
rect.setAttributeNS(null, 'width', symbolSide);
rect.setAttributeNS(null, 'height', symbolSide);
rect.setAttributeNS(null, 'x', -1 * Math.floor(symbolSide / 2));
rect.setAttributeNS(null, 'y', -1 * Math.floor(symbolSide / 2));
return rect;
} };
return {
size: { width: 80, height: 40 },
pivot: { x: 0, y: 0.5 },
first: function(bodyElm) {
var shapeChange = Kefir.emitter();
var allShapesGroup = document.createElementNS(SVG_XMLNS, 'g');
SHAPES.forEach(function(shape, i) {
var shapeGroup = document.createElementNS(SVG_XMLNS, 'g');
shapeGroup.appendChild(symbols[SHAPES[i]]());
shapeGroup.setAttributeNS(null, 'transform', 'translate(' + ((i * symbolSide * 2) + 10) + ', 0)');
shapeGroup.setAttributeNS(null, 'class', 'rpd-p5-shape-variant');
shapeGroup.addEventListener('click',
(function(shape) {
return function() {
variants[curShape].setAttributeNS(null, 'class', 'rpd-p5-shape-variant');
curShape = shape;
variants[curShape].setAttributeNS(null, 'class', 'rpd-p5-shape-variant rpd-p5-active-variant');
shapeChange.emit(curShape);
}
})(shape));
variants[shape] = shapeGroup;
allShapesGroup.appendChild(shapeGroup);
});
variants[curShape].setAttributeNS(null, 'class', 'rpd-p5-shape-variant rpd-p5-active-variant');
bodyElm.appendChild(allShapesGroup);
return { 'shape': { valueOut: shapeChange } };
}
}
});
// ============= Monkey-patch util/random renderer =============
var curSvgRandomRendererFunc = Rpd.allNodeRenderers['util/random'].svg;
var newSvgRandomRendererFunc = function(node) {
var returnObj = curSvgRandomRendererFunc.apply(this, arguments);
if (returnObj.size) {
returnObj.size.width = 80;
} else {
returnObj.size = { width: 80 };
}
var textElm;
returnObj.first = function(bodyElm) {
textElm = document.createElementNS(SVG_XMLNS, 'text');
textElm.innerText = textElm.textContent = '?';
bodyElm.appendChild(textElm);
};
returnObj.always = function(bodyElm, inlets, outlets) {
textElm.innerText = textElm.textContent = 'from ' + inlets.min + ' to ' + inlets.max;
};
return returnObj;
};
Rpd.allNodeRenderers['util/random'].svg = newSvgRandomRendererFunc;
})();