toolkit/jb/svg.js (643 lines of code) (raw):
var SVG_XMLNS = "http://www.w3.org/2000/svg";
var HTML_XMLNS = "http://www.w3.org/1999/xhtml";
var drawInnerCanvases = true;
Rpd.noderenderer('jb/preview', 'svg', function() {
//var myP5;
return {
size: { width: 55, height: 30 },
pivot: { x: 0, y: 0 },
first: function(bodyElm) {
/* var targetDiv = document.getElementById('rpd-jb-preview-target');
if (!targetDiv) {
targetDiv = document.createElement('div');
targetDiv.id = 'rpd-jb-preview-target';
document.body.insertBefore(targetDiv, document.body.childNodes[0]);
}
targetDiv.style.pointerEvents = 'none';
myP5 = new p5(initP5(window.innerWidth, window.innerHeight), targetDiv.id); */
},
always: function(bodyElm, inlets) {
//lastForms = inlets.forms;
//if (lastForms && lastForms.length)
//myP5.redraw();
}
};
});
Rpd.noderenderer('jb/save', 'svg', {
size: { width: 80, height: 40 },
first: function(bodyElm) {
var hiddenLink = d3.select(document.createElement('a'))
.attr('download', 'jetbrains-art.png');
var saveButton =
d3.select(bodyElm).append('text')
.style('font-size', '24px')
.style('text-anchor', 'middle')
.text('💾');
Kefir.fromEvents(saveButton.node(), 'mousemove').onValue(function() {
saveButton.style('font-size', '27px');
});
Kefir.fromEvents(saveButton.node(), 'mouseout').onValue(function() {
saveButton.style('font-size', '24px');
});
Kefir.fromEvents(saveButton.node(), 'click').onValue(function() {
var canvas = d3.select('#rpd-jb-preview-target .sketch-canvas').node();
if (!canvas) return;
//hiddenLink.attr('href', canvas.toDataURL('image/png'));
var blob = canvas.toBlob(function(blob) {
var url = URL.createObjectURL(blob);
hiddenLink.attr('href', url);
hiddenLink.node().click();
//URL.revokeObjectURL(url);
});
});
}
});
var lastCvsId = 0;
Rpd.noderenderer('jb/image', 'svg', function() {
var myP5, lastFile;
function getLastFile() { return lastFile; }
return {
size: { width: 200, height: 50 },
pivot: { x: 0, y: 0 },
first: function(bodyElm) {
var wrapperId = 'p5-canvas-' + lastCvsId;
var wrapper = createCanvasWrapper(wrapperId, bodyElm);
var node = this;
myP5 = new p5(createP5ForImageDrop(node, 'file', getLastFile), wrapper);
myP5.redraw();
lastCvsId++;
},
always: function(bodyElm, inlets) {
if (inlets.file) {
lastFile = inlets.file;
myP5.redraw();
}
}
};
});
function createCanvasWrapper(wrapperId, bodyElm) {
var group = document.createElementNS(SVG_XMLNS, 'g');
group.setAttributeNS(null, 'transform', 'translate(10, 10)');
var foreign = document.createElementNS(SVG_XMLNS, 'foreignObject');
var wrapper = document.createElementNS(HTML_XMLNS, 'div');
wrapper.id = wrapperId;
wrapper.className = 'p5-canvas-wrapper';
foreign.appendChild(wrapper);
group.appendChild(foreign);
bodyElm.appendChild(group);
return wrapper;
}
function createP5ForImageDrop(node, inletName, getFile) {
return function(p) {
p.preload = function() {};
p.setup = function() { var c = p.createCanvas(180, 30);
c.addClass('p5-canvas');
c.addClass('p5-drop-canvas');
c.addClass('rpd-jb-drop-allowed');
c.drop(function(file) {
if (file.type === 'image') {
node.inlets[inletName].receive(file);
}
});
p.background(100);
p.noLoop(); };
p.draw = function() {
var file = getFile();
if (file) {
p.background(255);
var image = maybeCachedImage(p, file);
p.image(image.hide(), 0, 0, 30, 30);
}
p.noStroke();
p.textSize(10);
if (!file) {
p.textAlign(p.CENTER);
p.fill(255);
p.text('Drag an image file onto this area.', p.width / 2, p.height / 2);
} else {
p.textAlign(p.LEFT);
p.fill(100);
p.text(file.name, 30, 18);
}
};
}
}
var PALETTE_NODE_WIDTH = PRODUCTS.length * 14 + 20;
var PALETTE_NODE_HEIGHT = 70;
var PALETTE_NODE_BODY_X = -(PALETTE_NODE_WIDTH / 2) + 10;
var PALETTE_NODE_BODY_Y = 5;
var LABEL_Y_SHIFT = 10;
Rpd.noderenderer('jb/palette', 'svg', function() {
var cellSide = 12;
return {
size: { width: PALETTE_NODE_WIDTH, height: PALETTE_NODE_HEIGHT },
first: function(bodyElm) {
var paletteChange = Kefir.emitter();
var productChange = Kefir.emitter();
var lastSelected, lastHilitedLabel;
var paletteGroups = [], labelText = {};
d3.select(bodyElm)
.append('g')
.call(function(rootGroup) {
var labels = rootGroup.append('g')
.attr('transform', 'translate(' + PALETTE_NODE_BODY_X +
', ' + ((-1 * PALETTE_NODE_HEIGHT / 2) + LABEL_Y_SHIFT) + ')');
PRODUCTS.forEach(function(product, i) {
labelText[product.id] =
labels.append('text')
.attr('class', 'rpd-jb-product-label')
.attr('transform', 'translate(' + (i * 14) + ', 0)')
.text(product.label);
});
})
.call(function(rootGroup) {
var palettes = rootGroup.append('g')
.attr('transform', 'translate(' + PALETTE_NODE_BODY_X +
', ' + PALETTE_NODE_BODY_Y + ')');
PRODUCTS.forEach(function(product, i) {
var palette = product.palette;
palettes.append('g')
.attr('class', 'rpd-jb-palette-variant')
.attr('transform', 'translate(' + (i * 14) + ', ' +
(-1 * (palette.length / 2 * cellSide)) + ')')
.call((function(palette, productId) { return function(paletteGroup) {
palette.forEach(function(color, i) {
paletteGroup.append('rect').attr('rx', 4)
.attr('x', 0).attr('y', i * cellSide)
.attr('width', cellSide).attr('height', cellSide)
.attr('fill', color);
});
Kefir.fromEvents(paletteGroup.node(), 'click').onValue(function() {
if (lastSelected) lastSelected.attr('class', 'rpd-jb-palette-variant');
if (lastHilitedLabel) lastHilitedLabel.attr('class', 'rpd-jb-product-label');
labelText[productId].attr('class', 'rpd-jb-product-label rpd-jb-active-label');
paletteGroup.attr('class', 'rpd-jb-palette-variant rpd-jb-active-variant');
lastSelected = paletteGroup;
lastHilitedLabel = labelText[productId];
paletteChange.emit(palette);
productChange.emit(product.id);
});
paletteGroups.push(paletteGroup);
} })(palette, product.id));
});
});
lastSelected = paletteGroups[0];
paletteGroups[0].attr('class', 'rpd-jb-palette-variant rpd-jb-active-variant');
return { 'palette': { valueOut: paletteChange },
'product': { valueOut: productChange } };
}
};
});
Rpd.noderenderer('jb/clear', 'svg', {
size: { width: 30, height: 25 },
first: function(bodyElm) {
var circle = d3.select(svgNode('circle'))
.attr('r', 9).attr('fill', 'black')
.style('cursor', 'pointer')
.style('pointer-events', 'all');
bodyElm.appendChild(circle.node());
var circleClicks = Kefir.fromEvents(circle.node(), 'click');
circleClicks.onValue(function() {
circle.classed('rpd-util-bang-fresh', true);
});
circleClicks.delay(500).onValue(function() {
circle.classed('rpd-util-bang-fresh', false);
});
return { 'trigger':
{ valueOut: circleClicks.map(function() { return {}; }) }
};
}
});
function svgNode(name) {
return document.createElementNS(SVG_XMLNS, name);
}
function prepareCanvas(myP5Canvas) {
myP5Canvas.className = 'p5-canvas';
}
// FIXME: almost the complete copy of util/knobs
var defaultKnobConf = {
speed: 1.5,
radius: 10,
width: 30, // radius * 2 + margin
height: 30,
//showIntTicks: false,
//stickToInts: false,
showGhost: true,
adaptAngle: null,
adaptValue: null
};
function createKnob(state, conf) {
var lastValue = 0;
//var state = { min: 0, max: 100 };
var adaptAngle = conf.adaptAngle || function(s, v) { return v * 360; };
return {
init: function(parent, valueIn) {
var hand, handGhost, face, text;
var submit = Kefir.emitter();
d3.select(parent)
.call(function(bodyGroup) {
face = bodyGroup.append('circle').attr('r', conf.radius)
.style('fill', 'rgba(200, 200, 200, .2)')
.style('stroke-width', 2)
.style('stroke', '#000');
handGhost = bodyGroup.append('line')
.style('visibility', 'hidden')
.attr('x1', 0).attr('y1', 0)
.attr('x2', 0).attr('y2', conf.radius - 1)
.style('stroke-width', 2)
.style('stroke', 'rgba(255,255,255,0.1)');
hand = bodyGroup.append('line')
.attr('x1', 0).attr('y1', 0)
.attr('x2', 0).attr('y2', conf.radius)
.style('stroke-width', 2)
.style('stroke', '#000');
text = bodyGroup.append('text')
.style('text-anchor', 'middle')
.style('fill', '#fff')
.text(0);
});
Kefir.fromEvents(parent, 'mousedown')
.map(stopPropagation)
.flatMap(function() {
if (conf.showGhost) handGhost.style('visibility', 'visible');
var values =
Kefir.fromEvents(document.body, 'mousemove')
//.throttle(16)
.takeUntilBy(Kefir.fromEvents(document.body, 'mouseup'))
.map(stopPropagation)
.map(function(event) {
var faceRect = face.node().getBoundingClientRect();
return { x: event.clientX - (faceRect.left + conf.radius),
y: event.clientY - (faceRect.top + conf.radius) };
})
.map(function(coords) {
var value = ((coords.y * conf.speed * -1) + 180) / 360;
if (value < 0) {
value = 0;
} else if (value > 1) {
value = 1;
}
return value;
});
values.last().onValue(function(val) {
lastValue = val;
handGhost.attr('transform', 'rotate(' + adaptAngle(state, lastValue) + ')')
.style('visibility', 'hidden');
submit.emit(lastValue);
});
return values;
})
.merge(valueIn || Kefir.never())
.onValue(function(value) {
var valueText = Math.floor(value * 100) / 100;
text.text(conf.adaptValue ? conf.adaptValue(valueText) : valueText);
hand.attr('transform', 'rotate(' + adaptAngle(state, value) + ')');
});
return submit.merge(valueIn ? valueIn : Kefir.never());
}
}
}
function initKnobInGroup(knob, target, id, count, width, height, valueIn) {
var submit;
d3.select(target).append('g')
.attr('transform', 'translate(0,' + ((id * height) + (height / 2) - (count * height / 2)) + ')')
.call(function(knobRoot) {
knob.root = knobRoot;
submit = knob.init(knobRoot.node(), valueIn);
});
return submit;
}
var LETTER_WIDTH = 15;
var BLENDS = [
//{ label: '•', name: 'NORMAL', value: 'N' },
{ label: '•', name: 'BLEND', value: 'B' },
//{ label: '•', name: 'ADD', value: 'A' },
//{ label: '•', name: 'DARKEST', value: 'K' },
//{ label: '•', name: 'LIGHTEST', value: 'L' },
{ label: '•', name: 'DIFFERENCE', value: 'D' },
//{ label: '•', name: 'EXCLUSION', value: 'X' },
{ label: '•', name: 'MULTIPLY', value: 'M' },
{ label: '•', name: 'SCREEN', value: 'S' },
//{ label: '•', name: 'REPLACE', value: 'R' },
{ label: '•', name: 'OVERLAY', value: 'O' }
//{ label: '•', name: 'HARD_LIGHT', value: 'H' },
//{ label: '•', name: 'SOFT_LIGHT', value: 'F' },
//{ label: '•', name: 'DODGE', value: 'G' },
//{ label: '•', name: 'BURN', value: 'U' }
];
var DEFAULT_LAYERS_BLENDS = [
/* layer-1: draw-pixels */ '', // normal
/* layer-2: apply-gradient */ 'O', //overlay, after: blend
/* layer-3: curves */ 'O', //overlay, after each: blend?
/* layer-4: shapes */ 'S', // overlay
/* layer-5: edges & squares */ 'O', // overlay
/* layer-6: back edges */ 'O', // overlay
/* layer-7: vignette */ 'O', // before: overlay, between: multiply, then: normal
/* layer-8: logo */ 'N' // normal
];
var DEFAULT_LAYERS_OPACITIES = [
1, /* layer-1: draw-pixels */
1, /* layer-2: apply-gradient */
0.5, /* layer-3: curves */
0.2, /* layer-4: shapes */
1, /* layer-5: edges & squares */
0.2, /* layer-6: back edges */
1, /* layer-7: vignette */
1, /* layer-8: logo */
1
];
var DEFAULT_BLEND = '';
var blendToIndex = {};
BLENDS.forEach(function(blend, i) {
blendToIndex[blend.value] = i;
});
function initBlendSwitchInGroup(target, id, count, width, height) {
var lastSelected;
var lastSelectedText;
var switchStates = {};
function switchBlend(blend, text) {
return function() {
if (lastSelected && (lastSelected.value !== blend.value) && lastSelectedText) {
switchStates[lastSelected.value] = false;
lastSelectedText.attr('fill', 'black');
}
if (!blend) return;
var selected = switchStates[blend.value] ? false : true;
switchStates[blend.value] = selected;
text.attr('fill', selected ? 'white' : 'black');
if (selected) {
lastSelected = blend;
lastSelectedText = text;
return blend.value;
} else {
lastSelected = null;
lastSelectedText = text;
return '';
}
};
}
var submit, text, clicks;
var texts;
d3.select(target).append('g')
.attr('transform', 'translate(0,' + ((id * height) + (height / 2) - (count * height / 2)) + ')')
.call(function(target) {
texts = BLENDS.map(function(blend, i) {
return target.append('text').style('cursor', 'pointer')
.text(blend.label)
.attr('transform', 'translate(' + (i * LETTER_WIDTH) + ',0)');
})
texts.forEach(function(text, i) {
text.append('title').text(BLENDS[i].name);
});
submit = Kefir.merge(
BLENDS.map(function(blend, i) {
return Kefir.fromEvents(texts[i].node(), 'click')
.map(switchBlend(blend, texts[i]));
})
);
});
if (DEFAULT_LAYERS_BLENDS[id]) {
var blendIdx = blendToIndex[DEFAULT_LAYERS_BLENDS[id]];
switchBlend(BLENDS[blendIdx], texts[blendIdx])();
}
return submit;
}
var LAYERS_COUNT = 9;
Rpd.noderenderer('jb/layers', 'svg', function() {
var count = LAYERS_COUNT;
var state = { min: 0, max: 1 };
var knobs = [];
for (var i = 0; i < count; i++) {
knobs.push(createKnob(state, defaultKnobConf));
}
var width = (BLENDS.length * LETTER_WIDTH) + 15 + defaultKnobConf.width;
var height = count * (defaultKnobConf.height - 5);
var modesX = (BLENDS.length * LETTER_WIDTH) - width - 3;
var knobsX = 10 + (width - defaultKnobConf.width) - (width / 2);
var shiftY = 13;
return {
size: { width: width,
height: height },
//pivot: { x: 0, y: 0.5 },
first: function(bodyElm) {
var valueOut = Kefir.pool();
var nodeRoot = bodyElm;
var knobsRoot = d3.select(nodeRoot).append('g')
.attr('transform', 'translate(' + knobsX + ',' + shiftY + ')')
.node();
var defaultValueStream;
var knobsOut = Kefir.combine(
knobs.map(function(knob, i) {
defaultValueStream = Kefir.constant(DEFAULT_LAYERS_OPACITIES[i]);
return initKnobInGroup(knob, knobsRoot, i, count, defaultKnobConf.width, defaultKnobConf.height - 5, defaultValueStream)
.merge(defaultValueStream);
// knob.init() returns stream of updates,
// so Kefir.combine will send every change
})
);
var switchersRoot = d3.select(nodeRoot).append('g')
.attr('transform', 'translate(' + modesX + ',' + shiftY + ')')
.node();
var blendsChanges = [];
for (var i = 0; i < count; i++) {
blendsChanges.push(initBlendSwitchInGroup(switchersRoot, i, count, 50, defaultKnobConf.height - 5)
.merge(Kefir.constant(DEFAULT_LAYERS_BLENDS[i] || DEFAULT_BLEND)));
}
var blendsOut = Kefir.combine(blendsChanges);
valueOut = knobsOut.combine(blendsOut).map(function(combined) {
return combined[0].map(function(opacity, index) {
return {
opacity: opacity,
blendMode: combined[1][index]
}
});
});
return {
'renderOptions': { valueOut: valueOut }
};
}
};
});
Rpd.noderenderer('jb/switch', 'svg', {
size: { width: 30, height: 40 },
first: function(bodyElm) {
var valueOut = Kefir.emitter();
var optionsCount = 2;
var text, textClicks;
var allClicks = [];
for (var i = 0; i < optionsCount; i++) {
text = d3.select(bodyElm).append('text')
.text(i + 1).attr('transform', 'translate(-5,' + ((i * 20) - 10) + ')')
.style('cursor', 'pointer');
textClicks = Kefir.fromEvents(text.node(), 'click');
textClicks.onValue((function(text) {
return function() {
text.classed('highlighted', true);
setTimeout(function() {
text.classed('highlighted', false);
}, 1000);
}
})(text));
allClicks.push(textClicks.map((function(i) { return function() { return i + 1; } })(i)));
}
return {
'value': { valueOut: Kefir.merge(allClicks) }
};
}
});
function addColorRect(parent, color, size) {
return d3.select(parent).append('rect')
.attr('width', size || 7).attr('height', size || 7)
.attr('rx', 1).attr('ry', 1)
.attr('fill', color || 'transparent')
.attr('stroke-width', 0.5)
.attr('stroke', 'white');
}
Rpd.channelrenderer('util/color', 'svg', {
show: function(target, value, repr) {
if (!value) return;
addColorRect(target, repr).classed('jb-color-value', true);
}
});
Rpd.channelrenderer('jb/palette', 'svg', {
show: function(target, value, repr) {
if (!value) return;
var groupNode = d3.select(target).append('g')
.classed('jb-palette-value', true)
.node();
value.forEach(function(color, i) {
addColorRect(groupNode, color).style('transform', 'translate(' + (i * 10) + 'px, 0)');
});
}
});
Rpd.noderenderer('jb/three-colors', 'svg', function() {
var group;
return {
first: function(bodyElm) {
group = d3.select(bodyElm).append('g')
.attr('transform', 'translate(-10, -30)');
},
always: function(bodyElm, inlets, outlets) {
if (!outlets.palette) return;
group.empty();
outlets.palette.forEach(function(color, i) {
addColorRect(group.node(), color, 20).style('transform', 'translate(0, ' + (i * 20) + 'px)');
});
}
};
});
function createP5ToDisplayPixels(node, trgWidth, trgHeight, getPixels) {
var srcWidth = window.innerWidth;
var srcHeight = window.innerHeight;
return function(p) {
var setupCalled = false;
p.preload = function() {};
p.setup = function() { var c = p.createCanvas(srcWidth, srcHeight);
c.addClass('p5-inner-canvas');
c.canvas.style.transform = 'scale(' + (trgWidth / srcWidth) + ',' +
(trgHeight / srcHeight) + ')';
p.noLoop();
setupCalled = true; }
p.draw = function() {
var pixels = getPixels();
if (!pixels) return;
p.loadPixels();
var source = pixels;
var target = p.pixels;
var d = p.pixelDensity();
for (var x = 0; x < srcWidth; x++) {
for (var y = 0; y < srcHeight; y++) {
for (var i = 0; i < d; i++) {
for (var j = 0; j < d; j++) {
pixelIdx = 4 * ((y * d + j) * srcWidth * d + (x * d + i));
target[pixelIdx] = source[pixelIdx];
target[pixelIdx+1] = source[pixelIdx+1];
target[pixelIdx+2] = source[pixelIdx+2];
target[pixelIdx+3] = source[pixelIdx+3];
}
}
}
}
p.updatePixels();
};
var prevRedraw = p.redraw;
p.redraw = function() {
if (!setupCalled) return;
prevRedraw.call(this);
};
}
}
function nodeWhichRendersPixels(width, height, cvsWidth, cvsHeight) {
return function() {
var myP5, lastPixels;
function getLastPixels() { return lastPixels; }
return {
size: { width: width, height: height },
pivot: { x: 0, y: 0 },
first: function(bodyElm) {
if (!drawInnerCanvases) return;
var wrapperId = 'p5-canvas-' + lastCvsId;
var wrapper = createCanvasWrapper(wrapperId, bodyElm);
var node = this;
myP5 = new p5(createP5ToDisplayPixels(node, cvsWidth, cvsHeight, getLastPixels), wrapper);
lastCvsId++;
},
always: function(bodyElm, inlets, outlets) {
if (!drawInnerCanvases) return;
if (outlets && outlets.pixels) {
lastPixels = outlets.pixels.values;
myP5.redraw();
}
}
};
}
}
if (drawInnerCanvases) {
Rpd.noderenderer('jb/noise', 'svg', nodeWhichRendersPixels(70, 200, 50, 40));
Rpd.noderenderer('jb/background', 'svg', nodeWhichRendersPixels(70, 150, 50, 40));
//Rpd.noderenderer('jb/draw-pixels', 'svg', nodeWhichRendersPixels(70, 100, 50, 40));
Rpd.noderenderer('jb/rorschach', 'svg', nodeWhichRendersPixels(70, 40, 50, 20));
Rpd.noderenderer('jb/rorschach-vertical', 'svg', nodeWhichRendersPixels(70, 40, 50, 20));
}
function createP5ToCallDrawable(node, trgWidth, trgHeight, getDrawable) {
var srcWidth = window.innerWidth;
var srcHeight = window.innerHeight;
var ctx;
var setupCalled = false;
var redrawsRequestedBeforeSetup = false;
return function(p) {
var prevRedraw = p.redraw;
p.preload = function() {};
p.setup = function() { var c = p.createCanvas(srcWidth, srcHeight);
c.addClass('p5-inner-canvas');
c.canvas.style.transform = 'scale(' + (trgWidth / srcWidth) + ',' +
(trgHeight / srcHeight) + ')';
ctx = c.drawingContext;
p.noLoop();
setupCalled = true;
if (redrawsRequestedBeforeSetup) {
//setTimeout(function() {
prevRedraw.call(p);
//}, 1);
}
}
p.draw = function() {
var drawable = getDrawable();
if (!drawable) return;
drawable.func(p, drawable.conf, ctx, { opacity: 1, blendMode: 'N' });
};
p.redraw = function() {
if (!setupCalled) {
redrawsRequestedBeforeSetup = true;
return;
}
prevRedraw.call(p);
};
}
}
function nodeWhichRendersDrawable(width, height, cvsWidth, cvsHeight) {
return function() {
var myP5, lastDrawable;
function getLastDrawable() { return lastDrawable; }
return {
size: { width: width, height: height },
pivot: { x: 0, y: 0 },
first: function(bodyElm) {
if (!drawInnerCanvases) return;
var wrapperId = 'p5-canvas-' + lastCvsId;
var wrapper = createCanvasWrapper(wrapperId, bodyElm);
var node = this;
myP5 = new p5(createP5ToCallDrawable(node, cvsWidth, cvsHeight, getLastDrawable), wrapper);
lastCvsId++;
},
always: function(bodyElm, inlets, outlets) {
if (!drawInnerCanvases) return;
if (outlets && outlets.drawable) {
lastDrawable = outlets.drawable;
myP5.redraw();
}
}
};
}
}
if (drawInnerCanvases) {
Rpd.noderenderer('jb/shapes', 'svg', nodeWhichRendersDrawable(70, 70, 50, 40));
Rpd.noderenderer('jb/apply-gradient', 'svg', nodeWhichRendersDrawable(70, 40, 50, 25));
Rpd.noderenderer('jb/vignette', 'svg', nodeWhichRendersDrawable(70, 70, 50, 40));
Rpd.noderenderer('jb/curved-edges', 'svg', nodeWhichRendersDrawable(70, 100, 50, 40));
Rpd.noderenderer('jb/edges-joints', 'svg', nodeWhichRendersDrawable(70, 100, 50, 40));
Rpd.noderenderer('jb/back-edges-squares', 'svg', nodeWhichRendersDrawable(70, 70, 50, 40));
Rpd.noderenderer('jb/draw-logo', 'svg', nodeWhichRendersDrawable(70, 70, 50, 40));
Rpd.noderenderer('jb/draw-pixels', 'svg', nodeWhichRendersDrawable(70, 70, 50, 40));
}