【问题标题】:Find arc center point if start point, end point and offset is given如果给定起点、终点和偏移量,则查找圆弧中心点
【发布时间】:2020-09-12 09:22:23
【问题描述】:

我想找到圆弧的中心点。 我有弧的起点、终点和偏移量。

我已经尝试了下面的代码。使用这段代码,我在 90% 的情况下得到了圆弧的中心点。但是对于某些数据,它无法找到中心点。

圆弧画我有这个定义

X169290Y2681101I90207J39371*
X267716Y2779527J98426*
X169290Y2877952I98425*
X79082Y2818897J98425*

以上四个圆心坐标的答案都是(4.299940599999999, 70.5999858)

这是一个圆弧图像,这个圆弧由四个单象限圆弧组成。

private Coordinate findCenter(Coordinate start, Coordinate end, Coordinate offset) {
        double twoPi = 2 * Math.PI;
        
        if (quadrantMode.equals("single-quadrant")) {
            // The Gerber spec says single quadrant only has one possible center,
      // and you can detect it based on the angle. But for real files, this
      // seems to work better - there is usually only one option that makes
      // sense for the center (since the distance should be the same
      // from start and end). We select the center with the least error in
      // radius from all the options with a valid sweep angle.
            
            double sqdistDiffMin = Double.MAX_VALUE;
            
            Coordinate center = null;
            
            List<Coordinate> qFactors = new ArrayList<Coordinate>();
            qFactors.add(new Coordinate(1, 1));
            qFactors.add(new Coordinate(1, -1));
            qFactors.add(new Coordinate(-1, 1));
            qFactors.add(new Coordinate(-1, -1));
            
            for (Coordinate factors : qFactors) {
                Coordinate testCenter = new Coordinate(start.getX() + offset.getX() * factors.getX(),
                        start.getY() + offset.getY() * factors.getY());
                // Find angle from center to start and end points
                double startAngleX = start.getX() - testCenter.getX();
                double startAngleY = start.getY() - testCenter.getY();
                double startAngle = Math.atan2(startAngleY, startAngleX);
                
                double endAngleX = end.getX() - testCenter.getX();
                double endAngleY = end.getY() - testCenter.getY();
                double endAngle = Math.atan2(endAngleY, endAngleX);
                
                // # Clamp angles to 0, 2pi
                double theta0 = (startAngle + twoPi) % twoPi;
                double theta1 = (endAngle + twoPi) % twoPi;
                
                // # Determine sweep angle in the current arc direction
                double sweepAngle;
                if (direction.equals("counterclockwise") ) {
                    theta1 += twoPi;
//                  sweepAngle = Math.abs(theta1 - theta0);
                    sweepAngle = Math.abs(theta1 - theta0) % twoPi;
                } else {
                    theta0 += twoPi;
                    sweepAngle = Math.abs(theta0 - theta1) % twoPi;
                }
                
                // # Calculate the radius error
                double sqdistStart = sqDistance(start, testCenter);
                double sqdistEnd = sqDistance(end, testCenter);
                double sqdistDiff = Math.abs(sqdistStart - sqdistEnd);
                
                // Take the option with the lowest radius error from the set of
                // options with a valid sweep angle
                // In some rare cases, the sweep angle is numerically (10**-14) above pi/2
                // So it is safer to compare the angles with some tolerance
                boolean isLowestRadiusError = sqdistDiff < sqdistDiffMin;
                boolean isValidSweepAngle = sweepAngle >= 0 && sweepAngle <= Math.PI / 2.0 + 1e-6;
                if (isLowestRadiusError && isValidSweepAngle) {
                    center = testCenter;
                    sqdistDiffMin = sqdistDiff;
                }
            }
            return center;
        }
        else {
            return new Coordinate(start.getX() + offset.getX(), start.getY() + offset.getY());
        }
    }

public static double sqDistance(Coordinate point1, Coordinate point2) {
    double diff1 = point1.getX() - point2.getX();
    double diff2 = point1.getY() - point2.getY();

    return diff1 * diff1 + diff2 * diff2;
}

圆弧绘制说明图

【问题讨论】:

  • 如果算法适用于大多数情况,但不适用于某些情况,它看起来像是一个基于准确性的问题。您是否调试了代码并发现了任何无法预料的值?
  • 如果偏移怎么办? X169290Y2681101I90207J39371 是什么意思?为什么另一个字符串只包含 I 或 J?
  • 您能详细说明输入格式吗?另外,圆弧是圆弧还是任意椭圆的弧?
  • @MBo I, J 表示 X 或 Y 方向的距离或偏移量的字符。 如果缺少 I 或 J,则使用 0 距离。
  • @Dominique,是的,我已经调试并在这一行发现了问题boolean isValidSweepAngle = sweepAngle &gt;= 0 &amp;&amp; sweepAngle &lt;= Math.PI / 2.0 + 1e-6; 我在哪里得到了这些值 "sweepAngle" =1.5708064868152167"Math 的值.PI / 2.0 + 1e-6" = 1.5707973267948965 对于这个微小的差异,它变成 isValidSweepAngle = false。

标签: java math geometry trigonometry coordinate-systems


【解决方案1】:

您正在处理由值1e-6 引起的准确性问题。我建议你做两件事:

  • 检查当您将此值替换为零时会发生什么(任何可能导致问题的副作用?)
  • 如果零值不起作用,请尝试用小于1e-6 的值替换,例如1e-91e-12、...

但最重要的是:您需要在修改该值时进行一些详尽的测试(您是否有需要覆盖的测试列表 + 发明一些实际的测试用例)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-21
    • 1970-01-01
    相关资源
    最近更新 更多