确保计算返回的坐标完全相同

Ensuring exact same coordinate returned by calculation

提问人:smpa01 提问时间:10/19/2023 最后编辑:smpa01 更新时间:10/19/2023 访问量:59

问:

我在一个大圆圈上以度角放置了 4 个小圆圈。我希望 javascript 返回完全相同的红色和蓝色圆圈(因为移位是水平的,y 是相同的,但 x 会改变)和绿色和粉红色圆圈的返回完全相同(因为移位是垂直的,x 是相同的,但 y 会改变)。0, 90, 180, 270yx

而下面的 javascript 返回相同的 x,但返回不同的 y(红色 = 237.76900370542438,蓝色 = 237.7690037054244)。展望未来,它搞砸了所有的计算,因为它们不一样。

我不知道这是什么根本原因,但有没有办法解决它?我需要它们完全相同。

const angle = [0, 90, 180, 270];
const angleCoord = [];

angle.forEach(
    (a, i) => {
        const element = d3.select('.circ1').node();
        const cx = parseFloat(element.getAttribute('cx'));
        const cy = parseFloat(element.getAttribute('cy'));
        const r = parseFloat(element.getAttribute('r'));
        const x = cx + Math.cos(a * Math.PI / 180) * r;
        const y = cy + Math.sin(a * Math.PI / 180) * r;
        angleCoord.push({
            angle: a,
            x: x,
            y: y
        })
    }
);

const color = ['red', 'green', 'blue', 'pink'];

d3.select('svg').append('g')
    .attr('class', 'circles')
    .selectAll('circle')
    .data(angleCoord)
    .join('circle')
    .attr('class', (_, i) => color[i])
    .attr('cx', d => d.x)
    .attr('cy', d => d.y)
    .attr('r', '4')
    .attr('fill', (_, i) => color[i]);
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
  <script type="text/javascript" src='https://cdn.jsdelivr.net/npm/[email protected]/bignumber.min.js'></script>
  <body>
    <div id="container" class="svg-container"></div>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 720">
      <circle class="circ1" cx="384" cy="237.76900370542438" r="122.23099629457562" fill="none" stroke="black"></circle>
    </svg>
    <script src="prod3.js"></script>
  </body>
</html>

JavaScript SVG 几何精度

评论

0赞 Wyck 10/19/2023
"它搞砸了所有的计算,因为它们不一样。>请举例说明由此引起的“计算混乱”。另请阅读: 浮点数学坏了吗?
1赞 Danny '365CSI' Engelman 10/19/2023
JavaScript 和浮点数不是最好的,请执行以下操作: 所以你有一些工作要做,才能在 JS 中变得“相同”console.log( 0.1 + 0.2 )

答:

0赞 Wyck 10/19/2023 #1

sin 180 的精度与 PI/2 的精度加上或减去一些无穷小误差的精度差不多。

如果您想要“精确”值,请考虑使用以度为单位的 90° 角倍数的 sin 和 cos 的“精确”值。例如:

const lookupTable = {
  0: { sin: 0, cos: 1 },
  90: { sin: 1, cos: 0 },
  180: { sin: 0, cos: -1 },
  270: { sin: -1, cos: 0 }
};

const exactSin = degrees => lookupTable[degrees].sin;
const exactCos = degrees => lookupTable[degrees].cos;

const angle = [0, 90, 180, 270];
const angleCoord = [];

const lookupTable = {
  0: { sin: 0, cos: 1 },
  90: { sin: 1, cos: 0 },
  180: { sin: 0, cos: -1 },
  270: { sin: -1, cos: 0 }
};

const exactSin = (degrees) => lookupTable[degrees].sin;
const exactCos = (degrees) => lookupTable[degrees].cos;

angle.forEach(
    (a, i) => {
        const element = d3.select('.circ1').node();
        const cx = parseFloat(element.getAttribute('cx'));
        const cy = parseFloat(element.getAttribute('cy'));
        const r = parseFloat(element.getAttribute('r'));
        const x = cx + exactCos(a) * r;
        const y = cy + exactSin(a) * r;
console.log({x, y});
        angleCoord.push({
            angle: a,
            x: x,
            y: y
        })
    }
);

const color = ['red', 'green', 'blue', 'pink'];

d3.select('svg').append('g')
    .attr('class', 'circles')
    .selectAll('circle')
    .data(angleCoord)
    .join('circle')
    .attr('class', (_, i) => color[i])
    .attr('cx', d => d.x)
    .attr('cy', d => d.y)
    .attr('r', '4')
    .attr('fill', (_, i) => color[i]);
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
  <script type="text/javascript" src='https://cdn.jsdelivr.net/npm/[email protected]/bignumber.min.js'></script>
  <body>
    <div id="container" class="svg-container"></div>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 720">
      <circle class="circ1" cx="384" cy="237.76900370542438" r="122.23099629457562" fill="none" stroke="black"></circle>
    </svg>
    <script src="prod3.js"></script>
  </body>
</html>

评论

0赞 smpa01 10/19/2023
在代码中是否有任何其他方法可以处理此问题?除了使用硬编码查找表之外。因为,对于产品,我不会事先知道所有的角度(因为它们都是动态的),因此不可能构建这样的表。
0赞 Wyck 10/19/2023
其他角度将不准确。您正在寻找的轴对称性吗?只需计算一个象限中的所有角度,然后使用上面的查找表将它们旋转到其他象限中。
0赞 Wyck 10/19/2023
@smpa01请更新您的问题,以更清楚地定义您的要求,即当所有角度都是动态的时,您希望结果是什么。
0赞 smpa01 10/19/2023 #2

感谢@Wyck分享指向另一个链接的链接。

我能够调整此链接中的建议,并在上述情况下使用它来通过让 js 在代码中处理它来解决问题。

const angle = [0, 90, 180, 270];
const angleCoord = [];
const color = ['red', 'green', 'blue', 'pink'];

function round(value, precision) {
  const power = Math.pow(10, precision)
  return Math.round((value*power)+(Number.EPSILON*power)) / power
}

angle.forEach(
    (a, i) => {
        const element = d3.select('.circ1').node();
        const cx = round(parseFloat(element.getAttribute('cx')),15);
        const cy = round(parseFloat(element.getAttribute('cy')),15);
        const r = round(parseFloat(element.getAttribute('r')),15);
        const _sin = round(Math.sin(a * Math.PI / 180),15);
        const _cos = round(Math.cos(a * Math.PI / 180),15);
        const x = cx + _cos * r;
        const y = cy + _sin * r;
        
        console.log(`${color[i]}`,cx,cy,r,_sin,_cos,x,y,);
        
        angleCoord.push({
            angle: a,
            x: x,
            y: y
        })
    }
);

d3.select('svg').append('g')
    .attr('class', 'circles')
    .selectAll('circle')
    .data(angleCoord)
    .join('circle')
    .attr('class', (_, i) => color[i])
    .attr('cx', d => d.x)
    .attr('cy', d => d.y)
    .attr('r', '4')
    .attr('fill', (_, i) => color[i]);
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
  <script type="text/javascript" src='https://cdn.jsdelivr.net/npm/[email protected]/bignumber.min.js'></script>
  <body>
    <div id="container" class="svg-container"></div>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1536 720">
      <circle class="circ1" cx="384" cy="237.76900370542438" r="122.23099629457562" fill="none" stroke="black"></circle>
    </svg>
    <script src="prod3.js"></script>
  </body>
</html>