Home » Android » math – Android – calculate arc angle

math – Android – calculate arc angle

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have an arc and i wish to draw scale marks at 0, 45, 90, 135, 180 degrees, can anyone help me with the math needed to achive the x,y of points 5 and 30 on this sketch?:

enter image description here

here is my code for drawing the 1 scale mark.

   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();
}
How to&Answers:

You can calculate its rotation using sin and cos. Lets assume that you have zero point A and want to rotate it to point B which is rotated for 30°.
Something like this:

enter image description here

Basically new point is at (cx+x,cy+y). In this particular case definition of sin and cos would be next:

sin = x/R
cos = y/R

It is not hard to get exact x and y. So to rotate point on particular angle in circle with know radius we need to calculate coordinates in next way:

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

Now lets get back to Android and 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();
}

Code will draw marks with step of 45°. Note you need to convert angle to radians and for Y axis I used minus cause on canvas it is flipped. Here is what I have got:

enter image description here

Answer:

If you know the point at the center of the circle and the radius of the circle it becomes pretty easy if you use vectors to your advantage.

First you’re gonna need the unit vectors at each angle

  • 0 deg -> (-1,0)
  • 45 deg -> (-1/sqrt(2), (1/sqrt(2))
  • 90 deg -> (0,1)
  • 135 deg -> (1/sqrt(2), (1/sqrt(2))
  • 180 deg -> (1,0)

You can then calculate the necessary points using the formula below

point = center + (unit vector * distance from center)

Here is a more concrete example since andrew added one.

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);
}

Here is the result of the code above
Result of the code