I need to create a custom d3 layout that is somewhat close to a treemap but in a triangular style. Here's a screenshot so that you can understand:
Pyramid layout
As you can see, it works pretty neat and fits my need.
To code it, i've based the code on the treemap layout code:
d3.layout.pyramid= function () {
var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = 0;
function populate (nodes, currentHeight, currentHeightPadded, currentBase, currentSumedWeight) {
...
}
function populate_layers (layer, nodes,currentHeight,currentLength, currentSumedArea,currentSumedWeight) {
...
}
function pyramid(d) {
var nodes = hierarchy(d), root = nodes[0];
populate(root.children.slice(),0,0,0,0);
return nodes;
}
pyramid.padding = function(x) {
if (!arguments.length) return padding;
padding = x;
return pyramid;
};
pyramid.size = function(x) {
if (!arguments.length) return size;
size = x;
return pyramid;
};
return d3_layout_hierarchyRebind(pyramid, hierarchy);
};
My problem is, to do so, I've had to directly edit the d3.v2.js file, because some private functions are not accessible from outisde, in my case d3_layout_hierarchyRebind.
Clearly I know it´s not the best practice at all but I can't manage to externalize my file in a separate script cause d3_layout_hierarchyRebindis not visible from the outside.
I don't know if it's a d3- or javascript-related issue but I'd like to know if you could help me solve this little problem.
Thank's in advance!
Just copy and paste the d3.layout.pyramid function into a new file and rename functions as necessary so it doesn't conflict with the d3 library. Likely everything will be private so only the outermost function will need to be renamed. You probably won't have to namespace it to "d3". That is to say, this should work:
var myPyramidLayout = function () {
...
}
Related
all. I have kind of a doozy of a problem, that could be solved really simply, if I just wanted to duplicate the code. I mean, really, it's a small part of a project that I'm doing just to see if I can, more than anything else, but it is bothering me since I've thought it up.
The Project
For fun, I've decided to take someone's ActionScript 3, text-based game engine and convert it to TypeScript and ultimately JavaScript using PixiJS.
The thing is, there are still 20213 errors to be fixed running tsc, so I could just leave this to a later date. But I was working on the Button class, which they defined as a subclass of MovieClip. That's fine; I just responded by reading up on PIXI buttons, and they seem fairly straightforward. Just, in the button's constructor, add something akin to the following lines:
export class Button extends PIXI.Sprite {
private _callback : Function;
private _height : number;
private _width : number;
public get callback() : Function { return this._callback; }
public set callback(fn : Function) {this._callback = fn; }
public get height() : number { return this._height; }
public set height(h : number) {this._height = h; }
public get width() : number {return this._width; }
public set width(w : number) {this._width = w; }
public constructor(width = 180, height = 90, callback: Function = null){
super(new PIXI.Texture(new PIXI.BaseTexture(GLOBAL.BTN_BACK, PIXI.SCALE_MODES.NEAREST)));
this.callback = callback;
this.width = width;
this.height = height;
this.buttonMode = true;
this.interactive = true;
this.anchor.set(0.5);
this.on('mousedown', this.callback)
.on('touchstart', this.callback);
}
}
That's a bit of a simplified version, and the version I did on Codepen uses a Container and a private _sprite field instead (as well as a ColorMatrixFilter that doesn't work too well on the black icons I picked out, but that's not really important for this question), but that's roughly the gist of how it's done.
The Problem
The problem is that, in the codepen, I'd like to do the following:
// assign `this.callback` to each of the following events:
let that = this;
['click','mousedown','touchstart'].map(evt => that.on(evt, that.callback});
with a simple call being passed in their constructors elsewhere:
for (let n = 0; n < 5; ++n){
btnArray.push(new Button(16, 16, () => console.info('You pushed button %d', n)));
}
but I'm not getting anything from them, even in the Chrome Console. I even logged that ColorMatrixFilter I mentioned earlier, to see if it was console.info that was wrong. Nope. So now, I'm confused on that. I was hoping to be able to just make a GLOBAL (a legacy static object from the AS source) key to iterate through for the events, but it looks like that's not happening.
The Questions
Is what I'm trying to do feasible, if odd? Is it blocked by a security feature (for which I'd be grateful)? If not, what am I doing wrong?
Should I even worry about setting all these different event handlers, or is just listening to click enough?
When an arrow function like your event map is executed the this context is not set, so any code that references this is going to get the current value, including any functions your map calls.
Replace your event map with the following:
['click','mousedown','touchstart'].map(function(evt) { that.on(evt, that.callback} } );
A demonstration:
function Named(x) {
this.name = x;
}
var foo = new Named("foo");
var bar = new Named("bar");
var showFunc = function show() {
// this is context dependant
console.log(this.name);
}
var showArrow;
// this is the window
showArrow = () => console.log(this.name);
var fooShowArrow;
(function() {
// this is foo
that = this;
fooShowArrow = () => console.log(that.name);
}).apply(foo);
var example = function(func) {
// For the demo, at this point, this will always be bar
func.apply(this, [ "arbitrary value" ]);
}
// explicitly set the current "this" to bar for the execution of these functions
example.apply(bar, [showFunc]); // works
example.apply(bar, [showArrow]); // fails, this is still the window
example.apply(bar, [fooShowArrow]); // fails, this is still foo
For the sake of simplicity, let's say I have two modules: table.js and chart.js. I would like to bundle them using WebPack, but I end up with an error, probably due to the fact that they are dependent on each other.
table.js:
var chart = require('./chart');
module.exports = (function () {
var init = function() {
_loadTable();
chart.init();
};
var update = function() {
console.log('table updated');
};
return {
init: init,
update: update
}
})();
chart.js:
var table = require('./table');
module.exports = (function () {
var init = function() {
_drawChart();
table.update(); // will throw an error: table.update is not a function
};
return {
init: init
}
})();
What happens here?
Table is loaded first by table.init() and then loads chart module by chart.init(). Chart is drawed and tries to update the table in return.
However, at this point chart.js doesn't know what is var table = require('./table') (because at that moment var table = {}), thus table.update() will throw an error.
This didn't happen before when modules were managed "traditionally" (loaded by script tags and communicated through global window object).
I am wondering, if WebPack can still be used for parallely-dependent modules like these?
This article provides a pretty exhaustive list of what you can do to solve your problem.
http://www.bitnative.com/2015/02/03/circular-dependencies-in-requirejs/
The inline form of the requires statement is probably the quickest fix, but has numerous drawbacks.
So, I am working on this website and have created multiple charts (which are all very similar, except for the data that I am plotting in each of them). Currently, I have javascript files, one for each of the charts, which contain mostly the same functions and variables (height, width, etc.) with the only difference being where the data is coming from and some other things.
However, I want to be able to include several of these javascript files into a webpage (so that all the charts show up together). But this will not work because there are common variable and function names in the javascript files (since they're pretty much the same scripts, as mentioned above). What would be the best way to avoid this conflict so that I can include multiple charts in the same webpage?
I am thinking it would be a good idea to factor out the common code and maybe have it so that each chart is an instance of some object. If each chart is an instance of, let’s say, a ‘Chart’ object, would that allow me to include multiple html/javascript files in the same webpage?
Apologies if the question is vague/unclear or has already been asked!
EDIT:
So I will post the specific code a little later, but my javascript files are essentially like this:
chartA.js:
var height = 10;
var width = 40;
var colors = ...;
var dataSource = chartA_data.json
function addDataToChart(source) {
//this adds the data in source to the html for chartA
}
addDataToChart(dataSource);
chartB.js:
var height = 10;
var width = 40;
var colors = ...;
var dataSource = chartB_data.json
function addDataToChart(source) {
//this adds the data in source to the html for chartA
}
addDataToChart(dataSource);
So, basically, the two js files are the same except for the dataSource variable. Would creating a chart object, with chartA and chartB as instances of the object, work?
Would creating a chart object, with chartA and chartB as instances of
the object, work?
It's definitely one way of doing it. The example is overly simplistic, but you will get the idea:
function Chart(args) {
this._name = args.name;
this._height = args.height;
this._width = args.width;
this._dataFetchingStrategy = args.dataFetchingStrategy;
//You may not want to call the rendering process immediately, but
//it was simpler for the example
this._render();
}
Chart.prototype = {
constructor: Chart,
_render: function () {
var el = this.el = document.createElement('div');
el.classList.add('chart');
el.style.height = this._height + 'px';
el.style.width = this._width + 'px';
el.appendChild(document.createTextNode(this._name));
this._contentEl = el.appendChild(document.createElement('div'));
this.refresh();
},
refresh: function () {
this._dataFetchingStrategy(function (data) {
this._contentEl.innerHTML = data;
}.bind(this));
}
};
[
new Chart({
name: 'Chart A',
height: 100,
width: 100,
dataFetchingStrategy: function (cb) {
cb('Data for chart A');
}
}),
new Chart({
name: 'Chart B',
height: 150,
width: 200,
dataFetchingStrategy: function (cb) {
setTimeout(cb.bind(null, 'Data for chart B'), 1500);
}
})
].forEach(function (chart) {
document.body.appendChild(chart.el);
});
.chart {
border: 1px solid grey;
}
Could some one please explain to me, why in d3-tip library (https://github.com/Caged/d3-tip), the object constructor looks like this:
d3.tip = function() {
var direction = d3_tip_direction,
offset = d3_tip_offset,
html = d3_tip_html,
node = initNode(),
svg = null,
point = null,
target = null
function tip(vis) {
svg = getSVGNode(vis)
point = svg.createSVGPoint()
document.body.appendChild(node)
}
tip.show = function() {
// some function
}
//...
return tip
}
To explain my confusion, I'm trying to replicate this library with different behaviour and can't make a decision, should I left this pattern as it is or change it into more standard creation function.
The function returns the tip function/object. at the bottom of the function it return tip; which is the new tip function.
by doing this the variables var direction = d3_tip_direction // etc are protected and are not accessible outside of the functions scope.
To understand further, you should look into design patterns, there is an excellent resource here
How can I unit-test Javascript that draws on an HTML canvas? Drawing on the canvas should be checked.
I wrote an example for unit-testing canvas and other image-y types with Jasmine and js-imagediff.
Jasmine Canvas Unit Testing
I find this to be better than making sure specific methods on a mock Canvas have been invoked because different series of methods may produce the same method. Typically, I will create a canvas with the expected value or use a known-stable version of the code to test a development version against.
As discussed in the question comments it's important to check that certain functions have been invoked with suitable parameters. pcjuzer proposed the usage of proxy pattern. The following example (RightJS code) shows one way to do this:
var Context = new Class({
initialize: function($canvasElem) {
this._ctx = $canvasElem._.getContext('2d');
this._calls = []; // names/args of recorded calls
this._initMethods();
},
_initMethods: function() {
// define methods to test here
// no way to introspect so we have to do some extra work :(
var methods = {
fill: function() {
this._ctx.fill();
},
lineTo: function(x, y) {
this._ctx.lineTo(x, y);
},
moveTo: function(x, y) {
this._ctx.moveTo(x, y);
},
stroke: function() {
this._ctx.stroke();
}
// and so on
};
// attach methods to the class itself
var scope = this;
var addMethod = function(name, method) {
scope[methodName] = function() {
scope.record(name, arguments);
method.apply(scope, arguments);
};
}
for(var methodName in methods) {
var method = methods[methodName];
addMethod(methodName, method);
}
},
assign: function(k, v) {
this._ctx[k] = v;
},
record: function(methodName, args) {
this._calls.push({name: methodName, args: args});
},
getCalls: function() {
return this._calls;
}
// TODO: expand API as needed
});
// Usage
var ctx = new Context($('myCanvas'));
ctx.moveTo(34, 54);
ctx.lineTo(63, 12);
ctx.assign('strokeStyle', "#FF00FF");
ctx.stroke();
var calls = ctx.getCalls();
console.log(calls);
You can find a functional demo here.
I have used a similar pattern to implement some features missing from the API. You might need to hack it a bit to fit your purposes. Good luck!
I make really simple canvases and test them with mocha. I do it similarly to Juho Vepsäläinen but mine looks a little simpler. I wrote it in ec2015.
CanvasMock class:
import ContextMock from './ContextMock.js'
export default class {
constructor (width, height)
{
this.mock = [];
this.width = width;
this.height = height;
this.context = new ContextMock(this.mock);
}
getContext (string)
{
this.mock.push('[getContext ' + string + ']')
return this.context
}
}
ContextMock class:
export default class {
constructor(mock)
{
this.mock = mock
}
beginPath()
{
this.mock.push('[beginPath]')
}
moveTo(x, y)
{
this.mock.push('[moveTo ' + x + ', ' + y + ']')
}
lineTo(x, y)
{
this.mock.push('[lineTo ' + x + ', ' + y + ']')
}
stroke()
{
this.mock.push('[stroke]')
}
}
some mocha tests that evaluates the functionality of the mock itself:
describe('CanvasMock and ContextMock', ()=> {
it('should be able to return width and height', ()=> {
let canvas = new CanvasMock(500,600)
assert.equal(canvas.width, 500)
assert.equal(canvas.height, 600)
})
it('should be able to update mock for getContext', ()=> {
let canvas = new CanvasMock(500,600)
let ctx = canvas.getContext('2d')
assert.equal(canvas.mock, '[getContext 2d]')
})
})
A mocha tests that evaluates the functionality of a function that returns a canvas:
import Myfunction from 'MyFunction.js'
describe('MyFuntion', ()=> {
it('should be able to return correct canvas', ()=> {
let testCanvas = new CanvasMock(500,600)
let ctx = testCanvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(0,0)
ctx.lineTo(8,8)
ctx.stroke()
assert.deepEqual(MyFunction(new CanvasMock(500,600), 8, 8), canvas.mock, [ '[getContext 2d]', '[beginPath]', '[moveTo 0, 0]', [lineTo 8, 8]', '[stroke]' ])
})
so in this example myfunction takes the canvas you passed in as an argument ( Myfunction(new CanvasMock(500,600), 8, 8) ) and writes a line on it from 0,0 to whatever you pass in as the arguments ( Myfunction(new CanvasMock(500,600),** 8, 8**) ) and then returns the edited canvas.
so when you use the function in real life you can pass in an actual canvas, not a canvas mock and then it will run those same methods but do actual canvas things.
read about mocks here
Since the "shapes" and "lines" drawn on a canvas are not actual objects (it's like ink on paper), it would be very hard (impossible?) to do a normal unit test on that.
The best you can do with standard canvas it analyze the pixel data (from the putImageData/getImageData. Like what bedraw was saying).
Now, I haven't tried this yet, but it might be more what you need. Cake is a library for the canvas. It's using alot of the putImageData/getImageData. This example might help with what you are trying to do with a test.
Hope that helps answer your question.
I've been looking at canvas testing recently and I've now thought about a page that allows comparing the canvas to a "known good" image version of what the canvas should look like. This would make a visual comparison quick and easy.
And maybe have a button that, assuming the output is OK, updates the image version on the server (by sending the toDataUrl() output to it). This new version can then be used for future comparisons.
Not exactly (at all) automated - but it does make comparing the output of your code easy.
Edit:
Now I've made this:
The left chart is the real canvas whilst the right is an image stored in a database of what it should look like (taken from when I know the code is working). There'll be lots of these to test all (eventually) aspects of my code.
From a developer's point of view the canvas is almost write-only because once drawn it's difficult to programmatically get something useful back. Sure one can do a point by point recognition but that's too tedious and such tests are hard to be written and maintained.
It's better to intercept the calls made to a canvas object and investigate those. Here are a few options:
Create a wrapper object that records all the calls. Juho Vepsäläinen posted a such example.
If possible use a library like frabric.js that offers a higher level of abstraction for drawing. The "drawings" are JS objects that can be inspected directly or converted to SVG which is easier to inspect and test.
Use Canteen to intercept all the function calls and attribute changes of a canvas object. This is similar with option 1.
Use Canteen with rabbit which offers you a few Jasmine custom matchers for size and alignment and a function getBBox() that can be used to determine the size and the position of the stuff being drawn on the canvas.