Android - 计算弧形角度

10

我有一个弧形,希望在0度、45度、90度、135度和180度处绘制刻度线,有人能帮我计算出图示中5和30点的x,y坐标吗?

enter image description here

这是我用来绘制1个刻度线的代码。

   private void drawScale(Canvas canvas) {
        //canvas.drawOval(scaleRect, scalePaint);

        canvas.save();

        Paint p = new Paint();
        p.setColor(Color.WHITE);
        p.setStrokeWidth(10f);
        canvas.drawLine(rectF.left-getWidth()/20, rectF.height()/2, rectF.left, rectF.height()/2, p);


    canvas.restore();
}
2个回答

19
你可以使用sincos来计算它的旋转。假设你有零点A,想要将其旋转到旋转了30度的点B。像这样:

enter image description here

基本上新点在(cx+x,cy+y)处。在这种特定情况下,sincos的定义如下:
sin = x/R
cos = y/R

获取准确的xy并不难。因此,要在已知半径的圆中旋转点,我们需要按以下方式计算坐标:

x = cx + sin(angle) * R; 
y = cy + cos(angle) * R;

现在让我们回到Android和Canvas!

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    float cx = getWidth() / 2f;
    float cy = getHeight() / 2f;

    float scaleMarkSize = getResources().getDisplayMetrics().density * 16; // 16dp
    float radius = Math.min(getWidth(), getHeight()) / 2;

    for (int i = 0; i < 360; i += 45) {
        float angle = (float) Math.toRadians(i); // Need to convert to radians first

        float startX = (float) (cx + radius * Math.sin(angle));
        float startY = (float) (cy - radius * Math.cos(angle));

        float stopX = (float) (cx + (radius - scaleMarkSize) * Math.sin(angle));
        float stopY = (float) (cy - (radius - scaleMarkSize) * Math.cos(angle));

        canvas.drawLine(startX, startY, stopX, stopY, scalePaint);
    }

    canvas.restore();
}

代码将以45度的步长绘制标记。请注意需要将角度转换为弧度,并且对于Y轴,我使用了负数,因为在画布上它被翻转了。以下是我的代码:

输入图像描述


1
不错,你可以使用Canvas.translate来设置中心点,这样代码会更加整洁。看起来你已经考虑到了这一点,因为你使用了saverestore来保存画布矩阵。 - weston
canvas.translate(-cx,-cy) 的翻译是:canvas.translate(-cx,-cy) - weston
@andrew 这些线条是颠倒的。如果我想要从下往上绘制线条,我该怎么办?请参考以下示例图片 - https://i.stack.imgur.com/IhrgK.png - Sureshkumar S
@android 不确定你的意思,请尝试调整半径或翻转 startX,startY,stopX,stopY - andrew

4
如果您知道圆的中心点和半径,并且使用向量来帮助您,那么这将变得相当容易。
首先,您需要在每个角度上使用单位向量: - 0度 -> (-1,0) - 45度 -> (-1/sqrt(2), (1/sqrt(2)) - 90度 -> (0,1) - 135度 -> (1/sqrt(2), (1/sqrt(2)) - 180度 -> (1,0)
然后,您可以使用以下公式计算必要的点。
point = center + (unit vector * distance from center)

这里有一个更具体的例子,因为安德鲁添加了一个。

private static final float RADIUS = 400.0f;
private static final float MARK_LENGTH = 30.0f;
private static final UnitVector[] UNIT_VECTORS = new UnitVector[] {
        new UnitVector(-1,0), // 0 deg
        new UnitVector((float) (-1/Math.sqrt(2)), (float) (1/Math.sqrt(2))), // 45 deg
        new UnitVector(0, 1), // 90 deg
        new UnitVector((float) (1/Math.sqrt(2)), (float) (1/Math.sqrt(2))), // 135 deg
        new UnitVector(1, 0), // 180 deg
        new UnitVector((float) (1/Math.sqrt(2)), (float) (-1/Math.sqrt(2))), // 225 deg
        new UnitVector(0, -1), // 270 deg
        new UnitVector((float) (-1/Math.sqrt(2)), (float) (-1/Math.sqrt(2))), // 315 deg
};

static class UnitVector {
    final float x;
    final float y;

    UnitVector(final float x, final float y) {
        this.x = x;
        this.y = y;
    }
}

// Call this from onDraw
public void drawMarks(final Canvas canvas) {
    for (final UnitVector unitVector : UNIT_VECTORS) {
        this.drawMarkWithVector(unitVector, canvas);
    }
}

private void drawMarkWithVector(final UnitVector unitVector, final Canvas canvas) {
    final float centerPointX = this.getWidth() / 2;
    final float centerPointY = this.getHeight() / 2;
    final float startX = centerPointX + (unitVector.x * RADIUS);
    final float startY = centerPointY + (unitVector.y * RADIUS);
    final float endX = centerPointX + (unitVector.x * (RADIUS + MARK_LENGTH));
    final float endY = centerPointY + (unitVector.y * (RADIUS + MARK_LENGTH));
    canvas.drawLine(startX, startY, endX, endY, this.paint);
}

这是上述代码的结果。 代码结果

首先,感谢您的帮助。其次,我并不是非常擅长这种数学问题,所以如果您能为我澄清以下几点,那就太好了:
  1. 什么是单位向量?
  2. 什么是点?
  3. 什么是标记长度? 再次感谢您的帮助。
- Ziv Kesten
一个简化思考单位向量的方法是将其视为长度为一的线,指向某个方向。这意味着如果您想要沿着单位向量的方向移动一个点一定的距离,只需将单位向量乘以所需的长度并加到原始点上即可。得到的点就是我在方程中提到的点。更多信息请参见https://www.mathsisfun.com/algebra/vector-unit.html。 标记长度是您正在绘制的白线的长度。 - Andre Compagno
添加了一个更具体的例子,因为安德鲁已经添加了一个。两种方法都很好,并且各有优点。安德鲁的解决方案更加灵活,可以改变标记绘制的角度。我的解决方案计算成本较低,因为它避免了三角函数运算。 - Andre Compagno
非常感谢您的帮助,很抱歉我选择了另一个答案作为正确答案,因为它更符合我的需求,但无论如何还是非常感谢您的帮助 :) - Ziv Kesten

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接