GithubHelp home page GithubHelp logo

How do you "clear" a brush? about d3-brush HOT 25 CLOSED

d3 avatar d3 commented on April 27, 2024
How do you "clear" a brush?

from d3-brush.

Comments (25)

mbostock avatar mbostock commented on April 27, 2024 10
selection.call(brush.move, null);

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 9

Sorry, but no, or at least not now. I am however considering creating an LLC to allow private licensing of my GPL’d code for a fee as a way to fund the ongoing development of D3. (I’m considering other funding models, too, but the added value of learning materials and examples seems like the most obvious way to offer a premium service on top of the free library.) It’s great that you are finding value from my software, but I can’t keep giving everything away forever.

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 5

I’ve updated this example to demonstrate brush snapping on end:

http://bl.ocks.org/mbostock/6232537

The main trick is to add this check to your end listener:

if (!d3.event.sourceEvent) return; // Only transition after input.

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 4

is there a reason brush.clear() was removed and replaced with this (which is not nearly as flexible as clear())

@sbromberger I assume you are asking what the underlying reason is, rather than implying I make random changes to the API without thought to the consequences.

The reason is that past versions of the brush didn’t cleanly specify where the brush state was stored. For the most part the state was stored on the brush object, but it was also necessary to store the brush state on selected elements to enable transitions (in element.chart). This led to bugs and confusion about the intended behavior. (Some of this is captured in d3/d3#1515.)

The D3 4.0 API clarifies that the brush state is always stored on the element. The brush is a rubber stamp (like d3-axis) that you apply to elements and re-apply to update. To access the brush’s selection you must use d3.event.selection within a brush event or d3.brushSelection, and to set the state you must use brush.move which takes a selection. (This design also eliminates the wart of brush.event in 3.x.)

You appear to argue that this:

selection.call(brush.move, null);

or equivalently this:

brush.move(selection, null);

is “way more complicated” and “not nearly as flexible” as this:

brush.clear();

But you don’t say why, so I don’t understand your criticism.

I argue that the new API is more flexible than the old one because it allows you to apply the brush event to multiple elements simultaneously, and is less complicated because the API doesn’t get confused about where the brush state is stored and whether multiple brushable elements share the same states. (In 3.x applying the brush to multiple elements was possible but buggy, as interaction would cause the other brushes to have detached states.) I suppose if you ignore the brokenness of the old API it might appear falsely simpler, but even then, it’s not particularly onerous to specify a selection whenever getting or setting the brush state.

If you meant that it’s complicated to call brush.move instead of a dedicated brush.clear method, I’d be willing to add a convenience method:

selection.call(brush.clear);

from d3-brush.

gnarf avatar gnarf commented on April 27, 2024 3

Thanks @mbostock !

Just to be 100% clear...

import {brush} from 'd3';
const myBrush = brush().on(...);

const brushArea = svg.append('g');
brushArea.call(myBrush);

// clear:
brushArea.call(myBrush.move, null);

// to move it programmatically
brushArea.call(myBrush.move, [[0,0], [1,1]]);

I was getting stuck trying to figure out how to use myBrush.move(brushArea, null) because it seems if I execute that code in my end handler, I end up with a start event being fired with a mouseup event as the current d3 event which is causing an execption.

Calling the brush.move in a setTimeout( () => brush.move(area, null), 0); causes a start brush event with d3.event === undefined

from d3-brush.

gnarf avatar gnarf commented on April 27, 2024 3

Still can't get the myBrush.move(brushArea, null) to work, I'm now getting a "call stack size exceeded" in d3.

from d3-brush.

gnarf avatar gnarf commented on April 27, 2024 2

❤️ thanks for all the help @mbostock

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024 1

Sorry, let me clarify - yes - I was wondering why the change was made (not that you had erred in making it).

In my case, it changes my code from:

    function brushend () {
      var extent = brush.extent();
      var xextent = extent.map(function (e) { return e[0]; });
      if (brush.empty()) {
        clicked += 1;
      } else {
        redrawChart(extent);
        d3.select('.brush').call(brush.clear());
      }
      if (clicked >= 2) {
        // zoom out code here
        clicked = 0;
      }
...

to

  b.end = () => {
    var extent = d3.event.selection;
    if (!d3.event.sourceEvent) return;
    if (extent === null) {
      b.clicked += 1;
      if (b.clicked >= 2) {
        b.chart.zoomout();
        b.clicked = 0;
      }
      return;
    }
    else {
      var xextent = extent.map(e => e[0]);
      b.chart.redraw(extent);
      ...
    }

    b.clear();
    b.create();
  }

  clear () {
    this.chart.brushArea.call(this.d3brush.move, null);
    this.d3brush = null;
    this.clicked = 0;
  }

 create () {
    var d3brush = d3.brush()
      .extent([[0,0], [this.chart.width, this.chart.height]])
      .on('start', this.start)
      .on('brush', this.move)
      .on('end', this.end)
    this.d3brush = d3brush;
    return d3brush;
  }
}

(The functionality I'm trying to achieve: brush to zoom in on a specific area of a chart; double-click to zoom out by a percentage.)

Which is significantly different and (at least to me) less intuitive.

Perhaps you have some suggestions as to how I might tighten up the new code?

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 1

Hard to tell from your code since those two snippets don’t seem comparable. But here’s my take on how to do it…

http://bl.ocks.org/mbostock/f48fcdb929a620ed97877e4678ab15e6

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 1

Thank you! Glad to hear you got it working. 🎉

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024 1

hey @mbostock - would you mind releasing that block under a BSD or MIT license (instead of, or in addition to, GPL)? I've integrated parts of it into this non-GPL code and would rather avoid a clean-room reimplementation.

Thanks :)

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 1

@jwdomingo Your question seems a bit off-topic for this issue and it would probably be more appropriate to use Stack Overflow or the d3-js Google Group to ask for help. To answer your question, yes, there is no longer a brush.empty method because (as described in the release notes and D3’s CHANGES.md) the brush no longer stores the selection state internally: it is stored on the element(s) you apply the brush to, and is accessible either as d3.event.selection or d3.brushSelection, which takes an element. If the selection is null then there is no selection.

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024 1

I’ve updated the documentation. (Though see also #15 for a bug if you clear the brush selection during an active brush gesture.)

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024

In general any programmatic control of the brush goes through brush.move. The general pattern is to use brush first to set up the event listeners:

selection.call(brush);

And then any time you want to set the brush extent programmatically (or manually trigger the brush event listeners), call brush.move.

from d3-brush.

mbostock avatar mbostock commented on April 27, 2024

There are multiple things going on here. See #9 for the issue regarding mouseup and undefined events.

As for the stack overflow, it sounds like you’re trying to set the brush within a brush event handler, which then triggers more brush events because you’re moving the brush. You need to prevent an infinite loop by not moving the brush in response to you moving the brush. There are a variety of ways to do that—for instance you can check if the brush is already in the position you want it to be in before moving it, or you can set a temporary boolean to check whether the brush event is being caused by your call to brush.move.

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024

@mbostock is there a reason brush.clear() was removed and replaced with this (which is not nearly as flexible as clear())? I have a similar issue - I want to clear the brush within end but this just seems way more complicated.

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024

Thanks, @mbostock - I'll see if I can adapt my code to yours. I appreciate the quick feedback.

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024

Your code is fantastic, btw. Thank you. I got it working.

from d3-brush.

curran avatar curran commented on April 27, 2024

Just want to add my $0.02 here on this point - having the GPL license on all the core example blocks makes D3 contract work tricky, as it requires each technique to be - as @sbromberger said - "clean-room reimplemented". Even then, some usage patterns (a few lines of code here and there) necessarily end up being exactly the same code as the original GPL block, which gives me a sickening feeling that I'm violating some kind of copyright law or something when I deliver code to clients (e.g. Global Migration in 2015).

from d3-brush.

sbromberger avatar sbromberger commented on April 27, 2024

@curran - wow, that visualization is beautiful!! 👍 💯

from d3-brush.

jwdomingo avatar jwdomingo commented on April 27, 2024

Hello! brush.empty() is no longer a function?

from d3-brush.

jwdomingo avatar jwdomingo commented on April 27, 2024

Thank you @mbostock, I will migrate over to SO.

from d3-brush.

mblandfo avatar mblandfo commented on April 27, 2024

It might be good to update the documentation to reflect that you can call selection.call(brush.move, null)

https://github.com/d3/d3-brush/blob/master/README.md#brush_move

from d3-brush.

alexlenail avatar alexlenail commented on April 27, 2024

Hmm. I'm getting

Uncaught TypeError: Cannot read property '0' of null

when I try to .transition(t).call(brush.move, null) but not when I just directly .call(brush.move, null). There's no active event from the user when this happens. Thoughts?

from d3-brush.

SumNeuron avatar SumNeuron commented on April 27, 2024

How can one remove a brush and all of its events?

// e.g. undo
selection.call(brush)

in essence I want a button to control whether or not the user can use a brush on the chart and whether or not the cross hairs, etc appear.

from d3-brush.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.