在乌龟程序中画一个圆圈

Drawing a circle in a turtle program

我目前正在处理由 Turtle 逻辑驱动的 Processing(如语言)草图(参见 https://en.wikipedia.org/wiki/Turtle_graphics)。这意味着我从当前坐标到提供的坐标画了一条线。这个提供的坐标将成为新的当前坐标。我想近似一个圆并使用三角函数编写了一段简单的代码。代码如下所示:

void drawCircle(int radius){
   // The circle center is radius amount to the left of the current xpos
   int steps = 16;
   double step = TWO_PI /steps;

   for(double theta = step; theta <= TWO_PI; theta += step){
      float deltaX = cos((float)theta) - cos((float)(theta - step));
      float deltaY = sin((float)theta) - sin((float)(theta - step));

      moveXY(deltaX*radius, deltaY*radius);
   }

}

程序逻辑简单。它将使用变量 theta 循环遍历一个圆中的所有弧度。步骤的数量将指示每个 theta 块的大小。然后它将计算由 theta 控制的圆中特定点的 x,y 值。然后它将减去前一个循环的 x,y 值(因此 theta-step)以获得它必须从该位置移动的量才能达到所需的 x,y 位置。它最终会将这些增量值提供给 moveXY 函数,该函数从当前点到提供的值画一条线,并将它们作为新的当前位置。

当使用有限的步骤时,该程序似乎运行良好。然而,当步数增加时,圆圈变得越来越像斐波那契螺旋线。我的猜测是,这是由于浮点数以及正弦和余弦计算不精确,并且每次迭代都会累加。

我是不是理解错了什么?我希望最终将此代码移植到 Javascript,因此我正在设计中寻找解决方案。使用 BigDecimal 可能不起作用,特别是因为它不包含自己的余弦和正弦函数。我提供了一些图片来详细说明问题。非常感谢任何帮助!

步数 16:

步数 32:

步数 64:

步数 128:

Float 和 sine/cosine 应该足够精确。问题是:你在飞机上的位置有多精确?如果此位置以像素为单位测量,则每个浮点值在每一步后都会四舍五入为整数。然后精度损失加起来。

在循环的每次迭代中,您都在计算增量而不考虑当前坐标是什么。如此有效,你是 "dead-reckoning",这总是不准确的,因为每一步都会累积错误。

既然你知道你想要一个圆,另一种方法是在每次迭代时,首先确定你想要到达的圆上的实际点,然后计算到达那里的增量 - 比如以下(但我必须承认我还没有测试过!):

void drawCircle(int radius){
   // The circle center is radius amount to the left of the current xpos
   int steps = 16;
   double step = TWO_PI /steps;

   float previousX = 0;
   float previousY = radius;

   for(double theta = step; theta <= TWO_PI; theta += step){
       float thisX = radius * sin((float)theta);
       float thisY = radius * cos((float)theta);

       moveXY(thisX - previousX, thisY - previousY);

      previousX = thisX;
      previousY = thisY;
    }

}