datavis-tech / reactive-vis

A library for reactive data visualizations (Work in progress)
MIT License
15 stars 0 forks source link

Scale #7

Open curran opened 8 years ago

curran commented 8 years ago

Part of #1

Ideally, the Scale mixin would be generic and able to handle all kinds of scales. This approach was taken in the Chiasm Charts Scale Mixin. It worked, but the code became terribly complicated.

Perhaps it would be better to take a more incremental approach and introduce mixins for each scale type. For example, ScaleLinear from Dynamic Baseball Scatter Plot:

function ScaleLinear(my, name){
  var scale = d3.scaleLinear();

  my(name + "ScaleDomain", function (data, accessor){
    return d3.extent(data, accessor);
  }, "data, " + name + "Accessor");

  if(name === "x"){
    my("xScaleRange", function (innerWidth){
      return [0, innerWidth];
    }, "innerWidth");
  } else if(name === "y"){
    my("yScaleRange", function (innerHeight){
      return [innerHeight, 0];
    }, "innerHeight");
  }

  my(name + "Scale", function(domain, range){
      return scale
        .domain(domain)
        .range(range)
        .nice();
    }, name + "ScaleDomain, " + name + "ScaleRange")

    (name + "Scaled", function(scale, accessor){
      return function (d){
        return scale(accessor(d));
      };
    }, name + "Scale, " + name + "Accessor");
}

I don't like having the special cases for X and Y there though. I wonder how those can be moved out of the scale mixin...

curran commented 8 years ago

What if the domain and range had to be specified externally? The Scale mixin could be reduced to something like this:

function ScaleLinear(my, name){
  var scale = d3.scaleLinear();

  my(name + "Scale", function(domain, range){
      return scale
        .domain(domain)
        .range(range)
        .nice();
    }, name + "ScaleDomain, " + name + "ScaleRange")

    (name + "Scaled", function(scale, accessor){
      return function (d){
        return scale(accessor(d));
      };
    }, name + "Scale, " + name + "Accessor");
}
curran commented 8 years ago

The plethora of scales in D3 4.0 should all be supported:

Object.keys(d3).filter(function (d){ return d.indexOf("scale") === 0; });
[
  "scaleBand",
  "scalePoint",
  "scaleIdentity",
  "scaleLinear",
  "scaleLog",
  "scaleOrdinal",
  "scaleImplicit",
  "scalePow",
  "scaleSqrt",
  "scaleQuantile",
  "scaleQuantize",
  "scaleThreshold",
  "scaleTime",
  "scaleUtc",
  "scaleCategory10",
  "scaleCategory20b",
  "scaleCategory20c",
  "scaleCategory20",
  "scaleSequential",
  "scaleCubehelix",
  "scaleRainbow",
  "scaleWarm",
  "scaleCool",
  "scaleViridis",
  "scaleMagma",
  "scaleInferno",
  "scalePlasma"
]

Maybe there could be some meta aspect like this:

function Scale(my, name, type){
  var scale = d3["scale" + type]();

  my(name + "Scale", function(domain, range){
      return scale
        .domain(domain)
        .range(range)
        .nice();
    }, name + "ScaleDomain, " + name + "ScaleRange")

    (name + "Scaled", function(scale, accessor){
      return function (d){
        return scale(accessor(d));
      };
    }, name + "Scale, " + name + "Accessor");
}

That .nice() in there should be configurable though...

curran commented 8 years ago

Maybe it would be better to pass in the scale instance from the outside, and have the Scale mixin only deal with setting the domain, range, and other configurable properties. Sometimes the range is part of the scale, as in sequential scales like d3.scaleSequential(d3.interpolateRainbow);