Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird behaviour when nesting brushes #35

Closed
mrbrahman opened this issue Feb 8, 2018 · 1 comment
Closed

Weird behaviour when nesting brushes #35

mrbrahman opened this issue Feb 8, 2018 · 1 comment

Comments

@mrbrahman
Copy link

mrbrahman commented Feb 8, 2018

I did go to stackoverflow first, but I think this one needs to be reported here.

I'm creating 2 nested brushes using d3. The inner brush is bound by the outer brush. I also limit the behavior of the brushes using filter function. Clicking on the outer brush selection creates the inner brush, and double clicking the selection of the inner brush removes it.

I see that the brushes behave perfectly in all but one instance.

If I squish the outer brush into the inner brush, and then try to alter the height of the inner brush, d3 is not respecting the extent of the inner brush. It seems like d3 is multiplying the height by 10, which seems to be causing this.

Below is the whole code:

<!DOCTYPE html>

<style>
.handle--w, .selection, .overlay {
  cursor: auto;
}

.brush2>.handle--n {
  cursor: auto;
}

.brush2>.selection {
  fill: red;
}

</style>
<svg width=1200 height=500></svg>

<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script type="text/javascript">
var rectHeight = 80, rectWidth = 180,
  svg = d3.select("svg"),
  mainGroup = svg.append("g");

var xScale = d3.scaleLinear()
    .domain([0,1])
    .range([0,rectWidth]);

// function for outer brush
var brush1 = d3.brushX()
    .extent([[0, 0], [rectWidth-10, rectHeight]])
    .handleSize([4])
    .filter(function(){
      // keep only the right side expansion/contraction
      return !d3.event.button
        && !d3.select(d3.event.target).classed("handle--w")
        && !d3.select(d3.event.target).classed("overlay")
        && !d3.select(d3.event.target).classed("selection")
        ;
    })
    .on("brush", brushed1)
    ;

// when brush1 is "move"d
function brushed1() {
  var w = d3.select(this).select(".selection").attr("width")-3;
  //console.log("in brushed1 -- "+w);
  if(!d3.select(this.nextSibling).empty()){
    // handle the inner brush, if present
    //console.log("found inner brush");
    var innerBrush = d3.select(this.nextSibling);
    brush2.extent([[0,0],[w,rectHeight]]);
    innerBrush.call(brush2);
    if (w < innerBrush.select(".selection").attr("width")){
      var innerHeight = innerBrush.select(".selection").attr("height");
      innerBrush.call(brush2.move, [[0,0],[w,innerHeight]]);
    }
  }
}

// function for inner brush
var brush2 = d3.brush()
  .handleSize([4])
  .filter(function(){
    return !d3.event.button
      && !d3.select(d3.event.target).classed("overlay")
      && !d3.select(d3.event.target).classed("selection")
      ;
  });

// create outer brush
mainGroup.append("g")
  .attr("class", "brush1")
  .call(brush1)
  .call(brush1.move, [0,0.65].map(xScale));

// add function for outer brush selection click
mainGroup.selectAll("rect.selection")
  .on("mousedown touchstart", function() {
    var w = d3.select(this).attr("width")-3;
    //console.log("Selection clicked  -- " + w);
    var p = d3.select(this.parentNode.parentNode);

    // Create inner brush if one doesn't exist
    if (d3.select(this.parentNode.nextSibling).empty()) {
      //console.log("creating element");
      //console.log(`w ${w} rectHeight ${rectHeight}`);
      brush2.extent([[0,0], [w,rectHeight]]);
      var innerBrush = p.append("g")
        .attr("class", "brush2")
        .call(brush2)
        .call(brush2.move, [[0,0],[w/2,rectHeight/2]])
        ;

      // remove inner brush if double clicked
      innerBrush.select("rect.selection")
        .on("dblclick touchstart", function(){
          //console.log("removing element");
          d3.select(this.parentNode).remove();
        })
    }
  });
</script>
@mbostock
Copy link
Member

mbostock commented Aug 2, 2019

One problem I see is that you are setting the height as a string rather than a number:

innerHeight = innerBrush.select(".selection").attr("height")

This is a coercion bug in d3-brush; we should be coercing that input to a number for you. As for the other issues, I’m not really sure I understand how a nested brush would work, or where the bug might be.

Thanks for the report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants