Подтвердить что ты не робот

Кисть на повернутых линиях с использованием d3 для создания эффекта масштабирования

Я работаю над этим plnkr. У меня есть три строки под углами 30, 45 и 60. Я хочу нанести кисть на эти строки, чтобы при рисовании графика линии перерисовывались там, где она пересекала матовый прямоугольник с соответствующими значениями на оси. Любая помощь или подсказка для решения этой проблемы очень ценится.

EDIT: Если у вас есть разные решения для рисования повернутых линий и кисти поверх них, это тоже приветствуется. Пожалуйста, помогите.

var ga = d3.select("svg")
    .append("g")
    .attr("class", "a axis")
    .attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
    .selectAll("g")
    .data([30, 45, 60])
    .enter()
    .append("g")
    .attr("class", "rotatedlines")
    .attr("transform", function(d) { return "rotate(" + -d + ")"; })
    .attr("stroke-width", 1)
    .attr("stroke", "black")
    .attr("stroke-dasharray", "5,5");

Пример результата

4b9b3361

Ответ 1

Чтобы объяснить мое решение:

Основные шаги, которые следует предпринять, заключаются в следующем:

  • обновить домены масштабов x и y до степени щетки
  • перерисовать оси
  • вычислить масштабный коэффициент и перевод для строк
  • масштабировать и переводить контейнеры линий соответственно.
  • reset кисть

Обратите внимание, что шаги 3 и 4 необходимы только потому, что вы не используете шкалы для рисования всего - лучший подход заключался бы в определении двух точек для каждой строки как данных, связанных с элементами, а затем использования шкал перерисовать. Это упростит код.

При вашем подходе все еще возможно. Чтобы облегчить это, я внесла несколько изменений в ваш код - в частности, я очистил различные вложенные элементы g с разными переводами и определил строки через их x1, x2, y1, y2, а не через перевод контейнеров. Оба этих изменения облегчают реализацию функциональности, поскольку требуется только одно преобразование, которое не требует рассмотрения нескольких других преобразований. Я также вложил строки в несколько элементов g, чтобы их можно было масштабировать и переводить более легко.

Теперь функция обработчика кистей выглядит следующим образом:

// update scales, redraw axes
var extent = brush.extent();
x.domain(brush.empty() ? x2.domain() : [ extent[0][0], extent[1][0] ]);
y.domain(brush.empty() ? y2.domain() : [ extent[0][1], extent[1][1] ]);
xAxisG.call(xAxis);
yAxisG.call(yAxis);

Этот код должен быть достаточно понятным - домены масштабов обновляются в соответствии с текущей протяженностью кисти, а оси перерисовываются.

// compute and apply scaling and transformation of the g elements containing the lines
var sx = (x2.domain()[1] - x2.domain()[0])/(x.domain()[1] - x.domain()[0]),
    sy = (y2.domain()[1] - y2.domain()[0])/(y.domain()[1] - y.domain()[0]),
    dx = -x2(x.domain()[0]) - x2.range()[0],
    dy = -y2(y.domain()[1]) - y2.range()[1];
d3.selectAll("g.container")
  .attr("transform", "translate(" + [sx * dx, sy * dy] + ")scale(" + [sx, sy] + ")");

Это сложная часть, основанная на новых доменах весов, нам нужно вычислить масштаб и перевод строк. Масштабирующими факторами являются просто отношение старой степени к новой степени (обратите внимание, что я сделал копии шкал, которые не изменены), то есть число больше 1. Перевод определяет смещение (0,0) координата и вычисляется через разность старой (0,0) координаты (я получаю это из диапазона исходных масштабов) и позицию нового происхождения домена в соответствии с исходными масштабами.

При одновременном применении перевода и масштабирования нам необходимо умножить смещения с помощью коэффициентов масштабирования.

// reset brush
brush.clear();
d3.select(".brush").call(brush);

Наконец, мы очистим кисть и reset, чтобы избавиться от серого прямоугольника.

Заполните демо здесь.

Ответ 2

Вы можете получить доступ к размеру кисти с помощью d3.event.target.extent(). Поток для рисования шкалы таков:

  • Установить масштаб
  • Установить ось
  • Нарисовать ось

Как только кисть будет выполнена, вам необходимо изменить масштаб, а затем повторно нарисовать ось в соответствии с текущим доменом x и y. Это то, что вы имели в виду?

Я немного очистил код и сделал небольшую демонстрацию: http://plnkr.co/edit/epKbXbcBR2MiwUOMlU5A?p=preview