Saturday, October 1, 2011

Android : Drawing View with shapes plotted in r theta (r,θ) relation

r = 0.875*width/2+0.125cos(12θ)
r - width/2*cos(4θ)
r - width/2
Say you want to create views which contains shares as shown above. In those each circle drawn in (r,θ) relation. I implemented following onDraw method with possible two options.
Option 1. Rotating canvas and drawing circles - this is direct use of (r , θ)
Option 2. Converting points into x,y plan coordinate and drawing the circle.


@Override
protected void onDraw(Canvas canvas) {
float width = getWidth()/2;
float height = getHeight()/2;
Paint paint = new Paint();
paint.setColor(Color.RED);
        //Option 1
long time = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 13;i++){
canvas.drawCircle(width, 0, width*0.2f, paint);
canvas.rotate(-15);
}
canvas.restore();
Log.i(TAG, "::onDraw:" + "time 1 = " + (System.currentTimeMillis()-time));
        //Option 2
long time1 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
float angle = 0;
for(int i = 0 ; i < 13;i++){
float radian = (float) (Math.PI*angle/180.0f);
canvas.drawCircle((float) (width*Math.cos(radian)),
(float) (width*Math.sin(radian)), width*0.2f, paint);
angle+=15;
}
canvas.restore();
Log.i(TAG, "::onDraw:" + "time 2 = " + (System.currentTimeMillis()-time1));
}

Since above coding draw only 13 circles, I couldn't find a clear different in execution time for both options. So I change the code to draw 180 circle as follows:


@Override
protected void onDraw(Canvas canvas) {
float width = getWidth()/2;
float height = getHeight()/2;
Paint paint = new Paint();
paint.setColor(Color.RED);
long time1 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 180;i++){
float radius = width;
canvas.drawCircle(radius, 0, 2.0f, paint);
canvas.rotate(-1);
}
canvas.restore();
time1 = System.currentTimeMillis()-time1;
long time2 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 180;i++){
float radian = (float) (Math.PI*i/180.0f);
float radius = width;
canvas.drawCircle((float) (radius*Math.cos(radian)),
(float) (radius*Math.sin(radian)), 2.0f, paint);
}
canvas.restore();
time2 = System.currentTimeMillis()-time2;
Log.i(TAG, "::onDraw:" + "time1,time2, (time1-time2) = "
+ time1 + ","+time2+","+(time1-time2));
}

Now i could see clear different and Option 2 is faster than Option 1.



INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 62,35,27
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 66,36,30
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 66,37,29
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 63,35,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 68,34,34
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 63,35,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 61,35,26
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 59,35,24
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 68,40,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 75,32,43
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 65,32,33
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 52,36,16


Here you have expresions for other above graphs. 

float radian = (float) (Math.PI*i/180.0f);

float radius = width;
float radius = (float) (width*Math.cos(4*radian));
float radius = (float) (0.875f*width+0.125f*width*Math.cos(12*radian));



But Option 2 needs some extra calculation to translate  (r , θ) into (x, y), in above examples we needed to determine only center of the circle.
Let see some of the view as follows.
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 72;i++){
canvas.drawLine(width*0.8F, 0, width*1.0f, 0, paint);
canvas.rotate(5);
}
canvas.restore();

Now we have to find start x,y and end x,y that need extra calculation and extra analysis effect to come up with that expresion.
Next one is more interesting need to use Path.

canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 18;i++){
Path path = new Path();
path.moveTo(width*0.8F, 0);
path.lineTo(width, width*0.1F);
path.lineTo(width, -width*0.1F);
path.lineTo(width*0.8F, 0);
canvas.drawPath(path, paint);
canvas.rotate(20);
}
canvas.restore();

Now you can find translation expression and can find out which one is faster in each cases.
Enjoy!

No comments: