首页 > 解决方案 > d3.js 拆分组,应用旋转和平移并计算新的平移坐标

问题描述

给定一个应用了平移和旋转变换的组中的初始矩形,我想用相同的旋转分割给定的矩形,并且考虑到旋转的平移。

下面是一个代码示例,试图解释我试图实现的目标。

我相信我只需要计算每个新矩形的平移 x,y 吗?

    //Draw initial svg
    const svg = d3.select('body').append('svg').attr('width', 1000).attr('height', 1000)

    // Data for the initial group with one rectangle inside
    const groupData = {width: 200, height: 100, translateX: 200, translateY: 150, rotation: 45}

    //Create the initial group
    let group1 = svg.append('g').attr('id', 'group1')

    //Append a rect to the initial group, translate and rotate
    group1.append('rect').attr('width', groupData.width).attr('height', groupData.height).attr('fill', 'blue')

    group1.attr('transform', `translate(${groupData.translateX}, ${groupData.translateY}) rotate(${groupData.rotation}, ${groupData.width / 2}, ${groupData.height / 2})`)



    // Attempt to split the initial group in equal parts based on the number of rows and columns from below
    const splitGroupData = {
      rows: 2,
      columns: 2
    }

    //Extract the data
    const {rows, columns} = splitGroupData

    // Create row and column array to dynamically split the initial group based on the split data
    const rowArray = [...Array(+rows).keys()]
    const columnArray = [...Array(+columns).keys()]

    //creates a rect inside the group 1 to split into 2 rows and 2 columns
    rowArray.forEach(row => {
      columnArray.forEach( column => {
        let newRect = group1
          .append("rect")
          .attr("width", groupData.width / columns)
          .attr("height", groupData.height / rows)
          .attr("x", (groupData.width / +columns) * column)
          .attr("y", (groupData.height / +rows) * row)
          .attr("stroke", "red")
          .attr("fill", "transparent");
      })
    })

    // Trying to recreate the same rect as above but within their own group and can't figure how to calculate the right translation

    rowArray.forEach(row => {
      columnArray.forEach( column => {
          
      let newGroup = svg.append('g').attr('id', `group${column}${row}`)

      newGroup.append('rect')
        .attr('width', groupData.width / columns)
        .attr('height', groupData.height / rows)
        .attr('fill', 'orange')

        const cx = groupData.width / 2;
        const cy = groupData.height / 2;
        let tmpX = groupData.translateX + (groupData.width / +columns) * column;
        let tmpY = groupData.translateY + (groupData.height / +rows) * row;
        tmpX = tmpX - cx;
        tmpY = tmpY - cy;
        const pointX =
          tmpX * Math.cos((groupData.rotation * Math.PI) / 180) -
          tmpY * Math.sin((groupData.rotation * Math.PI) / 180);
        const pointY =
          tmpX * Math.sin((groupData.rotation * Math.PI) / 180) +
          tmpY * Math.cos((groupData.rotation * Math.PI) / 180);
          
        newGroup.attr('transform', `translate(${groupData.translateX + pointX}, ${groupData.translateY + pointY}) rotate(${groupData.rotation}, ${groupData.width / columns / 2}, ${groupData.height  / rows / 2})`)
          
      })
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

编辑:现在我可以将每个新矩形定位在正确的位置上。在代码片段的示例中,我只想让所有 4 个矩形都覆盖初始矩形。

标签: d3.jssvg

解决方案


好的,所以我在多次手动测试后想通了。不确定它是否是最有效的公式,但至少它有效!希望它可以帮助遇到同样情况的人!

    //Draw initial svg
    const svg = d3.select('body').append('svg').attr('width', 1000).attr('height', 1000)

    // Data for the initial group with one rectangle inside
    const groupData = {width: 200, height: 100, translateX: 200, translateY: 150, rotation: 45}

    //Create the initial group
    let group1 = svg.append('g').attr('id', 'group1')

    //Append a rect to the initial group, translate and rotate
    group1.append('rect').attr('width', groupData.width).attr('height', groupData.height).attr('fill', 'blue')

    group1.attr('transform', `translate(${groupData.translateX}, ${groupData.translateY}) rotate(${groupData.rotation}, ${groupData.width / 2}, ${groupData.height / 2})`)

    const rows = 3
    const columns = 2
    // Create row and column array to dynamically split the initial group based on the rows and columns numbers
    const rowArray = [...Array(+rows).keys()]
    const columnArray = [...Array(+columns).keys()]

    rowArray.forEach(row => {
      columnArray.forEach( column => {
          
      let newGroup = svg.append('g').attr('id', `group${column}${row}`)

      newGroup.append('rect')
        .attr('width', groupData.width / columns)
        .attr('height', groupData.height / rows)
        .attr('fill', 'orange')

        const cx = groupData.translateX + (groupData.width / columns / 2) * (columns - 1);
        const cy = groupData.translateY + (groupData.height / rows / 2) * (rows - 1);
        const tmpX = groupData.translateX + (groupData.width / columns) * column - cx;
        const tmpY = groupData.translateY + (groupData.height / rows) * row - cy;
        const pointX = tmpX * Math.cos((groupData.rotation * Math.PI) / 180) - tmpY * Math.sin((groupData.rotation * Math.PI) / 180) + cx;
        const pointY = tmpX * Math.sin((groupData.rotation * Math.PI) / 180) + tmpY * Math.cos((groupData.rotation * Math.PI) / 180) + cy;


        newGroup.attr('transform', `translate(${groupData.rotation != 0 ? pointX : groupData.translateX + (groupData.width / columns) * column}, ${groupData.rotation != 0 ? pointY : groupData.translateY + (groupData.height / rows) * row}) rotate(${groupData.rotation}, ${groupData.width / columns / 2}, ${groupData.height  / rows / 2})`)
          
      })
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


推荐阅读