Нарисуйте измененные блоки, которые вращаются и переворачиваются с помощью d3.js

Мне нужно создать диаграмму, состоящую из иерархии блоков (большой блок, содержащий меньшие блоки, содержащие другие блоки).

Данные представляют собой иерархию этих блоков

{ 
    element: {name: test,  geometry: [..], orientation: '180'}
    element: {name: test2, geometry: [..], orientation: 'none'}
    element: {name: test3, geometry: [..], orientation: 'flipX'}
    element: { 
        name: test4, 
        geometry: [..], 
        orientation: '90'
        children:
            [ 
                element: {name: test5, geometry: [..], orientation: '180'}
                element: {name: test6, geometry: [..], orientation: 'none'}
            ]
        }
}

Каждый блок имеет геометрию (массив ребер) и ориентацию:

  • отсутствие ориентации
  • перевернуть на X (щелкнуть по центру ограничительной рамки по оси X)
  • перевернуть на y (перевернуть центр ограничительной рамки по оси Y)
  • поверните на 90 градусов (поверните вокруг точки начала 90 градусов)
  • 180 градусов

Координаты ребер относительно исходного родительского блока.

Итак, если главный блок повернут, система координат субблока также будет повернута.

Мне нужно нарисовать это, а затем изменить цвет заливки каждого блока на основе показателей.

Теперь я сделал это, чтобы рекурсивно разобрать эту иерархию и добавить элементы svg для каждого из них:

<svg>
    <g><path>
        <g><path></g>
        <g><path></g>
        <g><path>
            <g><path></g>
        </g>
    </g>
</svg>

Это помогает со всем наследованием координат, когда я рисую внутри групп, которые уже повернуты.

Я не уверен, что это лучший способ, поскольку я не использую функции .data() append() enter(), потому что я не знаю, как рисовать неотъемлемые элементы. Блоки также имеют метки и показатель того, где их происхождение, но я не включил это, чтобы упростить.

Есть ли лучший способ сделать это?

Спасибо!

Ответ 1

Пока вы не используете симуляцию, вам действительно не нужен вызов .data(). Вы можете использовать рекуррентную функцию для анализа дерева элементов и добавления элементов в конкретную группу SVG. Поскольку вы применяете преобразования в DOM, такие как вращение/масштабирование, я думаю, что наилучшим решением является использование DOM для имитации вашего дерева данных (это необходимо для поворотов и переворотов). Перевороты достигаются путем отрицательного масштабирования элемента DOM как такового:

if (orientation === 'flipX') {
    ref.attr('transform', 'scale(-1, 1) translate(${-ref.node().getBBox().width}, 0)');
}

if (orientation === 'flipY') {
    ref.attr('transform', 'scale(1, -1) translate(0, ${-ref.node().getBBox().height})');
}

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

Вот код, который позволит вам проанализировать дерево и добавить элементы DOM с определенными преобразованиями:

const svg = d3.select(svgDOM);
svg.selectAll("*").remove();
const group = svg.append('g');
group.attr('transform', 'translate(200,100)');

const colors = d3.schemeAccent;
let parseDataArr = (parent, arr) => {
  const group = parent.append('g');
  arr.forEach((elem, index) => {
    const {element: {geometry, orientation, children}} = elem;
    const ref = group.append('g');
    ref
      .append('path')
      .attr('fill', colors[index])
      .attr('opacity', 0.4)
      .attr('stroke', '#000')
      .attr('stroke-width', 1)
      .attr('d', 'M ${geometry.join('L')} z');

    if (["none", "flipX", "flipY"].indexOf(orientation) === -1) {
      ref.attr('transform', 'rotate(${orientation})');
    }

    if (children) {
      parseDataArr(ref, children);
    }

    if (orientation === 'flipX') {
      ref.attr('transform', 'scale(-1, 1) translate(${-ref.node().getBBox().width}, 0)');
    }

    if (orientation === 'flipY') {
      ref.attr('transform', 'scale(1, -1) translate(0, ${-ref.node().getBBox().height})');
    }

  });
}

parseDataArr(group, data);

Вот пример кода, который я использовал для тестирования реализации: https://observablehq.com/@cstefanache/test-svg-transforms