我重新实现 Processing 的 rotate() 方法时出现错误

Bug in my reimplementation of Processing's rotate() method

为了更好地理解 Processing 的绘图技术(尤其是 translate()rotate() 函数),我开始寻求重新实现 translate()rotate()point() 方法。

这样做的时候,我很快就遇到了游戏结束的情况,当时我完全卡在了 rotate() 方法上,它的行为简直是疯了。显示围绕多个轴的一些奇怪的 (3D) 旋转。

每个点旋转的代码:

x = x * c - y * s; // cos(a) * cos(b) - sin(a) * sin(b)
y = y * c + x * s; // sin(a) * cos(b) + cos(a) * sin(b)

完整代码

class MyRenderer {
  /* Added to every point - set through translation. */
  int xOffset = 0;
  int yOffset = 0;
  /** Prevents unnecessary rotations around an angle of 0. Owed to the architecture. */
  boolean rotated = false;

  /** The values of c(osine) and s(ine) in the unit circle, for the given angle.
   *  This way they don't have to be recalculated for every single point. */
  float c, s;

  private void myTranslate(final int x, final int y) {
    xOffset += x;
    yOffset += y;
  }

  /** Actual rotation, transforming each point's coordinates, done in myPoint() */
  private void myRotate(final int deg) {
    c = cos(radians(deg));
    s = sin(radians(deg));

    rotated = true;
  }

  /** Draws the points to the screen with respect to the set translation and rotation. */
  private void myPoint(float x, float y) {
    // First rotate...
    if (rotated) {  // Only do the transformation if a rotation actually occured.
      x = x * c - y * s; // cos(a) * cos(b) - sin(a) * sin(b)
      y = y * c + x * s; // sin(a) * cos(b) + cos(a) * sin(b)
    }
    // ... then translate.
    x += xOffset; y += yOffset;
    // Finally draw.
    point(x, y);
  }

  // Needed to reset the translation and rotation with every draw call - like the original.
  private int debugAngle = 0;
  public void myDraw() {
    reset();
    background(255);

    myTranslate(50, 50);
    myRotate(debugAngle++);
    // Point cloud in shape of a square.
    myPoint(0, 0);
    myPoint(-10, -10);
    myPoint(  0, -10);
    myPoint( 10, -10);
    myPoint( 10,   0);
    myPoint( 10,  10);
    myPoint( 0,   10);
    myPoint(-10,  10);
    myPoint(-10,   0);
  }

  /** Reset all scene transforms, mirroring the behavior of Processing's draw() method. */
  private void reset() {
    xOffset = 0;
    yOffset = 0;
    c = 1;  // cos for 0 degrees.
    s = 0;  // sin for 0 degrees.
    rotated = false;
  }
}


/* ========================================================================================= */

MyRenderer r;

void setup() {
  size(200, 100, FX2D);
  frameRate(30);
  r = new MyRenderer();
}

int debugAngle = 0;
void draw() {
  r.myDraw();
  debugRender();
}

/** Diplay a second 'rectangle' to compare the results of my renderer reimplementation to. */
void debugRender() {
  translate(100, 50);
  rotate(radians(debugAngle++));
  // Point cloud in shape of a square.
  point(0, 0);
  point(-10, -10);
  point(  0, -10);
  point( 10, -10);
  point( 10,   0);
  point( 10,  10);
  point( 0,   10);
  point(-10,  10);
  point(-10,   0);
}

在总结我的问题时,我发现了错误。写下来可能有帮助。还是很尴尬,时不时重蹈初学者的覆辙!

我还是上传了那个问题,因为我已经写完了。可能对我还不知道的人有帮助。


错误是覆盖 x 太快了,错误地使用了已经旋转的 x 值,而不是未转换的 x,导致 y 计算错误。

而不是

x = x * c - y * s; <== ERROR
y = y * c + x * s;

转换后的 x 必须被缓冲,直到所有依赖于原始 x 的计算完成:

float tmpX = x * c - y * s;
      y    = y * c + x * s;
      x    = tmpX;