dc-js / dc.js

Multi-Dimensional charting built to work natively with crossfilter rendered with d3.js
Apache License 2.0
7.43k stars 1.8k forks source link

multiple brushes in scatter plot brushing #1288

Open cmuus opened 7 years ago

cmuus commented 7 years ago

It would be great to be able to draw multiple brushes in https://dc-js.github.io/dc.js/examples/scatter-brushing.html

in short: -make a brush -hold shift to add another brush, while shift is pressed have ability of adding more brushes. -click on the graph and reset

gordonwoodhull commented 7 years ago

Interesting idea @cmuus. I assume you mean that the brushes would be additive, that is to say the filter would be the union of the brushed selections?

(As opposed to having multiple separate selections, which is really cool but not something that crossfilter's data model supports.)

cmuus commented 7 years ago

Yes that's exactly what I mean. Shouldn't be super hard to implement, right? I don't have much experience with dc.js but happy to help.

gordonwoodhull commented 7 years ago

Well it's a little different from the way dc.js currently works, where there is one brush object which determines one filter. It directly manipulates the corners of the RangedTwoDimensionalFilter.

The simplest thing to do would be to sense the shift key and in that case add a new filter instead of modifying the existing one.

I think you can do this without modifying dc.js by replacing the scatterPlot's _brushing function:

    _chart._brushing = function () {
        var extent = _chart.extendBrush();

        _chart.redrawBrush(_chart.g());

        if (_chart.brushIsEmpty(extent)) {
            dc.events.trigger(function () {
                _chart.filter(null);
                _chart.redrawGroup();
            });

        } else {
            var ranged2DFilter = dc.filters.RangedTwoDimensionalFilter(extent);
            dc.events.trigger(function () {
                _chart.filter(null);
                _chart.filter(ranged2DFilter);
                _chart.redrawGroup();
            }, dc.constants.EVENT_DELAY);

        }
    };

https://github.com/dc-js/dc.js/blob/9c5bc3d49540f51bccfdd746fdf91f2dcd20446d/src/scatter-plot.js#L383-L403

I don't see anything here that uses private members of the scatterPlot, so you should be able to just assign a new version of this function before rendering. You'd use d3.event to look at the current modifier keys and decide whether to replace filters or add them.

Please give it a shot and see if it's enough to get the behavior right. I see that there's one other change needed for the display: in plotData, it's only looking at the first filter when deciding which symbols to highlight/fade to show the selection. This could probably be patched up with a pretransition event handler.

It's possible that there are other places where we assume there is exactly one filter.

If you get stuck, just give me a holler and I'll try to work up an example in a fiddle - I don't have time today, and I'm always eager to enlist enthusiastic new recruits. 😉

It would be super-cool to add this feature to the library, but that takes some rigor (Jasmine tests, careful thought, etc.), so the first step is to try it as an external modification first.

cmuus commented 7 years ago

Ok great even better if it doesn't require to change the library. Thanks a lot for your quick replies.