1

我怀疑有某种方法可以在转换后使用元素的转换矩阵来计算其坐标,但我不知道该怎么做。

一个示例图最好地解释了这一点:


具有点 A、B 和旋转中心的圆。 A 是参考/起点。 B 与 A 点类似,但围绕旋转中心进行了旋转。 (以上所有内容都在一个包含的 SVG 中,它在各个方向上都大得多。不能假设圆的左边界框与根 SVG 的边界框匹配。) A:我知道到旋转中心的距离,如以及精确的 x 和 y 坐标。 B:我可以通过 B.getCTM() 得到一个变换矩阵,但它的 x 和 y 坐标与 A 的相同。使用 B 的变换矩阵,我需要确定变换后的 x 和 y 坐标。 换句话说,我需要能够创建一个与 B 出现在同一位置但不应用任何转换的元素。


恐怕我没有将数学路线带入编程。我可以理解转换矩阵在做什么的表面细节,但我的理解有点像一次能读一个音符的音乐,而且速度很慢;我对更高层次的曲调不是很了解,所以我对一个完整的乐句的声音也不是很了解——更不用说旋律了。

同样,我不明白转换矩阵是如何工作的。我曾尝试寻找 grok 转换矩阵的解释,但我发现的所有内容都包含更多我不理解的数学术语。我只知道它们的工作方式有点像函数,而且它们是非常灵活的工具,但仅此而已。

在所有可用的方法中SVGMatrix(据说不推荐使用DOMMatrix,但 Firefox Dev. ed. 仍在使用SVGMatrix),我不知道是否.inverse()或者.multiply()是我想要的,也不知道如何哄出一组简单的xy坐标矩阵。

笔记:

  • 我对在这里将屏幕转换为 SVG 坐标不感兴趣。
  • 我只关心 SVG(用户空间)坐标。
4

2 回答 2

1

您可以使用简单的三角变换:

const rotatePoint = (point, center, rotateAngle) => {
  const dx = point.x - center.x;
  const dy = point.y - center.y;
  const distance = Math.hypot(dx, dy);
  const currentAngle = Math.atan(dy / dx);
  const nextAngle = currentAngle - rotateAngle;
  const nextDX = distance * Math.cos(nextAngle);
  const nextDY = distance * Math.sin(nextAngle);
  return {x: center.x + nextDX, y: center.y + nextDY};
};

该片段显示一个蓝色点围绕红色点的旋转(逆时针 30 / 90 / 123 度)

const rotatePoint = (point, center, angle) => {
    const dx = point.x - center.x;
  const dy = point.y - center.y;
  const distance = Math.hypot(dx, dy);
  const current = Math.atan(dy / dx);
  const next = current - angle;
  const nextDX = distance * Math.cos(next);
  const nextDY = distance * Math.sin(next);
  return {x: center.x + nextDX, y: center.y + nextDY};
};

const center = {x: 150, y: 150};
const start = {x: 200, y: 30};
const svg = d3.select('svg');

svg.append('circle')
    .attr('cx', center.x)
  .attr('cy', center.y)
  .attr('r', 5)
  .style('fill', 'red');
svg.append('circle')
    .attr('cx', start.x)
  .attr('cy', start.y)
  .attr('r', 5)
  .style('fill', 'blue');  

// Rotate 30 deg
const p30 = rotatePoint(start, center, Math.PI * 30 / 180); 
svg.append('circle')
    .attr('cx', p30.x)
  .attr('cy', p30.y)
  .attr('r', 5)
  .style('fill', 'green');  
// Rotate 90 deg
const p90 = rotatePoint(start, center, Math.PI * 90 / 180); 
svg.append('circle')
    .attr('cx', p90.x)
  .attr('cy', p90.y)
  .attr('r', 5)
  .style('fill', 'orange');
// Rotate 123 deg
const p123 = rotatePoint(start, center, Math.PI * 123 / 180); 
svg.append('circle')
    .attr('cx', p123.x)
  .attr('cy', p123.y)
  .attr('r', 5)
  .style('fill', 'yellow');  
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="250" height="200"></svg>

于 2021-04-03T14:56:05.217 回答
1

一旦你有了一个 SVG Circle,

getPointAtLength您可以使用标准和getTotalLength函数计算圆上的任何 SVG 点。

请注意圆圈开始 绘制的位置(绿色点)

pathLength 转换为度数:

<style>
  svg{ height:300px }
  text {
    font-size: 3px;
    fill: white;
    text-anchor: middle;
    dominant-baseline: middle;
  }
</style>
<svg id="SVG" viewBox="0 0 100 100">
  <circle cx="50%" cy="50%" r="50%" fill="pink"></circle>
  <circle id="CIRCLE" cx="50%" cy="50%" r="40%" 
          fill="none" stroke="black" stroke-dasharray="2"></circle>
  <circle cx="90%" cy="50%" r="5%" fill="green"></circle>
</svg>
<script>
  let circle_length = CIRCLE.getTotalLength();
  for (let degree = 0; degree < 360; degree += 45) {
    let { x , y } = CIRCLE.getPointAtLength(  degree * ( circle_length / 360 )  );
    let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
    group.innerHTML = `<circle cx="${x}" cy="${y}" r="3%" fill="blue"/>` +
                      `<text x="${x}" y="${y}">${degree}</text>`;
    SVG.append(group)
  }
</script>

于 2021-04-03T17:52:28.043 回答