krispo / angular-nvd3

AngularJS directive for NVD3 reusable charting library (based on D3). Easily customize your charts via JSON API.
http://krispo.github.io/angular-nvd3
MIT License
1.29k stars 377 forks source link

Overriding tick listener on force directed graph #688

Open bubblechartsordeath opened 7 years ago

bubblechartsordeath commented 7 years ago

I'd like to create a force directed graph in which the nodes can't move out of the viewport. I understand that this can be achieved by modifying the tick listener function to keep the x and y values within a certain range like in this example. However, I'd like to do this without modifying the nvd3 source code in my workspace. Does anyone know of a way to provide our own tick listener for this chart type?

I've tried the following:

Thanks for any help/suggestions you can give!

simon-lang commented 6 years ago

@bubblechartsordeath - I know this is over a year old, but did you ever figure out a solution for this?

I think I'll need to directly use d3 for my force graph if I can't modify the tick listener.

bubblechartsordeath commented 6 years ago

Hi @simon-lang, I ended up going with the custom d3.timer (the 3rd approach I listed in my original post). It works fine until your chart is completely filled with nodes. At that point, the resistance between the nodes mixed with the "auto correction" of the custom d3 timer causes the nodes around the border to do a very jittery dance that's tough to look at.

I have the following code within my "on-ready" callback that I registered with the nvd3 directive. The "chartData" and "options" objects are the values that I set in the "data" and "options" nvd3 directive attributes. I believe "radius" is a value that I had manually set on each node. In my case the nodes had a dynamic size but you may be able to get away with a constant if that's not the case for you.

//create d3 timer for keeping nodes within visible chart
d3.timer(function() {
    if (this.options.chart.width && this.options.chart.height) {
        _.forEach(this.chartData.nodes, (d) => {
            d.x = Math.max(d.radius, Math.min(this.options.chart.width - d.radius, d.x));
            d.y = Math.max(d.radius, Math.min(this.options.chart.height - d.radius, d.y));
            d.px = Math.max(d.radius, Math.min(this.options.chart.width - d.radius, d.px));
            d.py = Math.max(d.radius, Math.min(this.options.chart.height - d.radius, d.py));
        });
    }
}.bind(this));
simon-lang commented 6 years ago

This is great @bubblechartsordeath , thanks for taking the time!