How to tell if two d3 scales are equivalent or if the d3 scale has changed?

I am using React with d3 and I am trying to figure out how to properly trigger the render on zoom. However, it is difficult to reliably determine if the scale has changed.

In theory, there are two ways I can go to solve the problem.

I can regenerate the scales on every render, and then have a reliable way of determining if two scales are the same (e.g. compare their domains, ranges, etc.). But there are still some problems - for example, I'm not sure how to distinguish between linear scale and log scale with the same domains and ranges. (If I try a test value - say the midpoint of a domain - it works if a non-numeric scale is given. I could have avoided this, for example, by "ducking back" all d3 different scales, but that seems extremely tedious and possibly performance if the test function gets too trade-off.)

Another solution would be to keep the scale object constant for rendering, but somehow get the scale to trigger a render if any of its properties changed. I think it will end up the same as in the first variation, but maybe there is some trick in d3 that I don't know about making it easier?

+3


source to share


1 answer


(Warning: this is a hacky solution extremely to know what scale you have. If you don't like this, please let me know and I will remove it)

Each scale in D3 v4.x has a function copy

that provides a scale type. For example, this is a copy

linear scale function :

scale.copy = function() {
    return copy(scale, linear());
};

      

This is a copy

range function :

scale.copy = function() {
    return band()
        .domain(domain())
        .range(range)
        .round(round)
        .paddingInner(paddingInner)
        .paddingOuter(paddingOuter)
        .align(align);
};

      

Etc...

As you can see, the type scales are always present in the return value (if you are using the unminified version of D3).



So we can use that to compare scales (besides comparing domain and ranges as you are doing now). Here's an example:

var scale1 = d3.scaleBand();
  
var scale2 = d3.scaleLinear();
  
var scale3 = d3.scaleLinear();
  
console.log("Is scale1 the same of scale2? " + (scale1.copy.toString() === scale2.copy.toString()))
console.log("Is scale1 the same of scale3? " + (scale1.copy.toString() === scale3.copy.toString()))
console.log("Is scale2 the same of scale3? " + (scale2.copy.toString() === scale3.copy.toString()))
      

<script src="https://d3js.org/d3.v4.min.js"></script>
      

Run code


And this can be used to test different scales that have the same domain and range (as you asked in your question):

var scale1 = d3.scaleLinear()
  .domain([10, 20])
  .range([10, 20]);

var scale2 = d3.scaleLog()
  .domain([10, 20])
  .range([10, 20]);

console.log("Is scale1 the same of scale2? " + (scale1.copy.toString() === scale2.copy.toString()))
      

<script src="https://d3js.org/d3.v4.min.js"></script>
      

Run code


EDIT: Instead of comparing, copy

this is a better idea comparing the whole function. As @altocumulus pointed out in the comments section , some scales return the same copy

.

+1


source







All Articles