Создание шкалы asinh (обратного гиперболического синуса) в d3.js v4

Это будет замена шкалы журнала, чтобы она могла иметь дело с отрицательными числами. Я не видел много примеров пользовательских масштабов, хотя я пытался использовать источник логарифмической шкалы d3 в качестве отправной точки.

Ответ 1

Насколько я знаю, нет способов сделать пользовательские шкалы в D3 (по крайней мере, не в том смысле, который вы ищете). Все шкалы D3 масштабируются в два этапа:

  • с использованием домена, деинтерполируйте вход с заданной функцией деинтерполяции
  • используя интервал, интерполируйте промежуточный результат с шага 1, чтобы получить выход

Я верю, что ваш идеальный ответ в основном ответит на вопрос, "как установить функцию деинтерполяции шкалы D3 в пользовательскую функцию?" , и я не думаю, что в настоящее время это возможно.

Однако вы можете установить интерполяционную функцию. Этот пример из Майка Бостока показывает, как установить интерполяцию с помощью одной из встроенных функций облегчения D3: http://bl.ocks.org/mbostock/56ea94205411ee9e4dbec3742f7ad08c

Этот пример имеет эффект "рыбий глаз", который, вероятно, является противоположностью того, что вы хотите. Вы можете использовать функцию ослабления полинома, d3.easePolyInOut, с показателем меньше единицы, чтобы приблизиться к масштабированию журнала (см. Мой фрагмент кода). К сожалению, нет "logInOut" или "asinhInOut", поэтому, если вам нужен более крутой роллофф (чем полином), вам придется написать свою собственную функцию ослабления/интерполяции.

var data = Array.from(Array(21), (_,i)=>{return 10*(i-10)})

var svg = d3.select("svg"),
    margin = {top: 50, right: 20, bottom: 5, left: 20},
    width = svg.attr("width") - margin.left - margin.right,
    height = svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var polyexp = 0.25

var x = d3.scaleLinear()
    .domain([-100,100])
    .range([0, width])
    .interpolate(easeInterpolate(d3.easePolyInOut.exponent(polyexp)));

g.append("g")
    .attr("class", "axis axis--x")
    .call(d3.axisBottom(x));

g.selectAll("circle").data(data).enter().append("circle")
  .attr("cx", (d) => x(d))
  .attr("cy", -10)
  .attr("r", 3)
  .attr("fill", "steelblue")

function easeInterpolate(ease) {
  return function(a, b) {
    var i = d3.interpolate(a, b);
    return function(t) {
      return i(ease(t));
    };
  };
}
.axis text {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.js"></script>
<svg width="600" height="100"></svg>

Ответ 2

Лучшим примером в документации, по-видимому, является функция интерполяции масштаба. См. https://github.com/d3/d3-scale/blob/master/README.md#continuous-scales

var color = d3.scaleLinear()
    .domain([10, 100])
    .range(["brown", "steelblue"])
    .interpolate(d3.interpolateHcl);

Я не эксперт d3, поэтому надеюсь, что это поможет.