使用d3.js绘制旋转和翻转的交错块。

11

我需要创建一个包含一系列块层次结构的图表(一个大块,包含更小的块以及其他块)。

数据是这些块的层次结构。


{ 
    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() 函数,因为我不知道如何绘制嵌套元素。这些块还带有标签和它们原点的指示,但我没有包含这些内容以简化代码。

是否有更好的方法来完成这个任务?

谢谢!


你的数据能否使用d3 treemap模型进行描述? - ccprog
我认为它做不到,我的几何图形是固定的,我无法改变块的形状。 - Daniel
我认为最好的方法是创建一个以选择作为参数的函数,然后使用.selectAll().data()进行绑定,并使用.call()将该选择与您的新函数一起使用。该函数可以调用自身。如果您可以分享您的数据和当前状态,我可以尝试帮助您解决这个问题。 - Hugues Stefanski
1个回答

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接