Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable brush resize (DC.js, D3.js)

Tags:

d3.js

dc.js

Brush extent needs to be changed only from a dropdown as shown here: https://jsfiddle.net/dani2011/67jopfj8/3/

Need to disable brush extending by:

1) Extending an existing brush using the handles/resize-area of the brush

Gray circles are the handels:

enter image description here

2) Dragging a new brush by clicking on the brush background, where the haircross cursor appears.

JavaScript file

Removed the handles of the brush:

timeSlider.on('preRedraw',function (chart)
    {
        var timesliderSVG = d3.select("#bitrate-timeSlider-chart").selectAll("g.brush").selectAll("g.resize").selectAll("*").data(data[0]).exit().remove();})

If using css instead:

#bitrate-timeSlider-chart g.resize {
           display:none;
           visibility:hidden;

Now it looks like this:

enter image description here.

The rect and the path elements inside "resize e","resize w" were removed:

enter image description here

However,the "resize e", "resize w" for extanding the brush still exist:

enter image description here

g.resize.e and g.resize.w dimesions are 0*0:

enter image description here

Furthurmore,after deleting "resize e","resize w" in the "developer tools/elements" in chrome,they reappear after moving the brush.

Tried to remove the resize-area in brushstart,brush,brushend:

timeSlider.on('renderlet', function (chart) {
var brushg = d3.select("#bitrate-timeSlider-chart").selectAll("g.brush");
var resizeg = brushg.selectAll("g.resize").selectAll("*").data(data[0]);       
var timesliderSVG4 = 
brushg.on("brushstart", function () {resizeg.exit().remove()}).on("brush", function () { resizeg.exit().remove() }).on("brushend", function () {resizeg.exit().remove() })

dc.js file

Tried to change setHandlePaths,resizeHandlePath:

1) Remarked the _chart.setHandlePaths(gBrush):

_chart.renderBrush = function (g) {....
 //   _chart.setHandlePaths(gBrush);
 ...}

2) Changed _chart.setHandlePaths = function (gBrush) for example by removing the gbrush.path:

  //  gBrush.selectAll('.resize path').exit().remove();

3) Remarked/changed _chart.resizeHandlePath = function (d) {...}.

d3.js file

1) Remarked/changed resize such as:

mode: "move" //mode: "resize" ,

var resize = g.selectAll(".resize").data(resizes[0], d3_identity); 

Using resizes[0] disable the brush rendering on the background but still can re-extend the existing brush

2) Remarked/changed d3_svg_brushResizes

3) In d3.svg.brush = function():

a) Added .style("display","none"):

background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("display", "none").style("cursor", "none");

b) background.exit().remove(); The cursor now is "pointer" instead of "haircross" extending the brush to a full width

c) d3_svg_brushCursor disabled makes the whole brush disappear

4) Changed the pointer-events as specified here: https://developer.mozilla.org/en/docs/Web/CSS/pointer-events

5) console.log in different places to track the different brush events:

function d3_event_dragSuppress(node) {        
        console.log("here2 ");          
    }
    if (d3_event_dragSelect) {
        console.log("here3 d3_event_dragSelect");
        ...
    }
    return function (suppressClick) {
        console.log("suppressClick1");
        ...
            var off = function () {
                console.log("suppressClick2");
               ...
            w.on(click, function () {
                console.log("suppressClick3")
               ...    
    function d3_mousePoint(container, e) {
    console.log("d3_mousePoint1")
    ...
    if (svg.createSVGPoint) {
        console.log("createSVGPoint");
       ...
            if (window.scrollX || window.scrollY) {
                console.log("createSVGPoint1");
                svg = d3.select("body").append("svg").style({
                    ...

    function dragstart(id, position, subject, move, end) {
        console.log("dragstart")
       ...
            function moved() {
                console.log("moved ");

             console.log("transition1");
...
          if (d3.event.changedTouches) {
            console.log("brushstart1");
           ...
        } else {
            console.log("brushstart2");
           ..
        if (dragging) {
            console.log("dragging4");
           ....
            if (d3.event.keyCode == 32) {
                if (!dragging) {
                    console.log("notdragging1");
                    ...
        function brushmove() {
            console.log("brushmove");
            ...
            if (!dragging) {
                console.log("brushmove!dragging");
                if (d3.event.altKey) {
                    console.log("brushmove!dragging1");
                    ...
            if (resizingX && move1(point, x, 0)) {
                console.log("resizeXMove1");
              ...

            if (resizingY && move1(point, y, 1)) {
                console.log("resizeYMove1");
               ...
            if (moved) {
                console.log("moved");
                ...
        }
        function move1(point, scale, i) {
            if (dragging) {
                console.log("dragging1");
                ...
            if (dragging) {
                console.log("dragging2");
            ...
            } else {
                console.log("dragging10");
                ...
            if (extent[0] != min || extent[1] != max) {
                console.log("dragging11");
                if (i) console.log("dragging12"); yExtentDomain = null;    
                console.log("dragging13");            
        function brushend() {
            console.log("brushend");
        ...

The two changes that seemed to get closest to the needed result are in d3.js:

1) Using resizes[0] disables the brush rendering on the background but still can re-extend the existing brush

var resize = g.selectAll(".resize").data(resizes[0], d3_identity); 

2) Removing the brush's background changes the cursor to "pointer" instead of "haircross",extending the brush to a full width only when clicking on the graph

`background.exit().remove();`

Any help would be very appreciated!

like image 291
Dani Avatar asked Dec 16 '16 04:12

Dani


1 Answers

This is from the accepted answer in Disable d3 brush resize, as suggested by @altocumulus. I didn't see a response from @Dani on this idea in particular, but thought I'd go ahead and try it, since I've seen other people try it in the past. (Probably on the dc.js users group.)

It looks a little twitchy, because d3.js will draw the brush at the new extent, and then a moment later we reset the extent to what we want, but functionally it seems to do what we want.

In dc.js the function that handles brush "rounding" is coordinateGridMixin.extendBrush:

_chart.extendBrush = function () {
    var extent = _brush.extent();
    if (_chart.round()) {
        extent[0] = extent.map(_chart.round())[0];
        extent[1] = extent.map(_chart.round())[1];

        _g.select('.brush')
            .call(_brush.extent(extent));
    }
    return extent;
};

Notice that it's following the same pattern as Lars' answer. Well, this is sort of like rounding, right? Let's override it.

First, let's store the current number of hours when it's set through the dropdown:

var graphSpan;
function addHours(amountHours) {
  graphSpan = amountHours;
  // ...

Next let's override coordinateGridMixin.extendBrush:

timeSlider.extendBrush = function() {
    var extent = timeSlider.brush().extent();
    if(graphSpan) {
        extent[1] = moment(extent[0]).add(graphSpan, 'hours');
    }
    return extent;
}

We just replace the function. If we needed to reuse the old implementation in our function, we could use dc.override.

If graphSpan has been set, we add that amount to the beginning to get the end. If it's not set, we allow the user to specify the brush width - you'd need to default the graphSpan and the select widget if you wanted that part to work automatically.

Well, let's admit it: it's very twitchy, but it works:

twitchy fixed brush extent

EDIT: Got rid of the twitch! The problem was that dc.js was setting the brush extent asynchronously after a little while (in response to the filter event). If we also set it during extentBrush then it never shows the wrong extent:

timeSlider.extendBrush = function() {
    var extent = timeSlider.brush().extent();
    if(graphSpan) {
      extent[1] = moment(extent[0]).add(graphSpan, 'hours');
      timeSlider.brush().extent(extent);
    }
    return extent;
}

nicer brush resize

https://jsfiddle.net/gordonwoodhull/xdo05chk/1/

like image 199
Gordon Avatar answered Oct 09 '22 22:10

Gordon