Extraordinary Animations using Trigonometry and Coroutines (Radar Animation)

In the previous article we flew some jets to fight our enemies , in this article we will focus how to defend our country from enemies so we will create a radar to detect enemy jets.
note: you may need knowledge of canvas drawings in android as prerequisites for this article I’ll recommend to read below article before proceeding with this one article1, article2, article3, article4.
Being Defensive
To create our radar I will draw a pie shape like below and then I can rotate this pie and it will look as a radar is scanning

To draw this shape I need the midpoint of the view and it’s height and width to draw the lines and need a path to draw the pie shape , we can do this onSizeChaned()
.
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
midPoint = width / 2f
path.addArc(0f, 0f, width / 1f, height.toFloat(), -90f, 90f)
path.lineTo(width / 2f, width / 2f)
}override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
with(canvas) {
drawPath(path, alphaPaint)
drawLine(
midPoint,
midPoint,
width.toFloat(),
midPoint,
linePaint)
drawLine(midPoint,
midPoint,
midPoint,
0f,
linePaint)
}
}
here we are adding an arc from mid point to the edges of the screen it has an angle of 90 degrees we are also adding a line to path to close this path then we are invalidating to induce a call to onDraw()
to draw our path and lines, then in the onDraw()
we are drawing path and lines.
Couroutines for Animation
Using coroutines for animation is common in compose world but not much in the view world , let’s see how we can use coroutines in the view world.
var rotationAngle = 0fCoroutineScope(context = Dispatchers.Default).launch {
while (true) {
delay(10)
++rotationAngle
invalidate()
}
}
Here I am creating a coroutine starting an infinite loop and increasing the rotation angle by 1 degrees and invalidating after every 10 milliseconds to induce a call to onDraw()
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
with(canvas) {
//rotating canvas 1 degrees in every 10 millis
rotate(rotationAngle, midPoint, midPoint)
drawPath(path, alphaPaint)
drawLine(
midPoint,
midPoint,
width.toFloat(),
midPoint,
linePaint)
drawLine(midPoint,
midPoint,
midPoint,
0f,
linePaint)
}
}

well this is not enough fast jets can easily trespass from one part while the pie is scanning other part. I would like to add another layer of scanning to increase our security
For that I will draw a circle which continuously grows and shrinks in size, so how do we do that?
Trigonometry to the rescue
If you were like me in the high school thinking that when in life I am going to use this complex mathematics , I hated maths 😆
We will find answer to the old question now, because it turns out that we can use some high school trigonometry to solve our above problem.

If you remember this is sine graph in the above image the range of sine is [-1,1]
means you can keep on increasing the angle but the value of sine will keep on oscillating between -1 and 1, it goes from -1 to 1 and 1 to -1 as evident by the above graph.
So if I take an angle variable whose value is incremented in our coroutine then take the sin I will keep getting value from -1 to 1 and if I multiply these values to width/2
I can get the radius of my circle whose range will come be [-width/2,width/2]
, a negative radius does not make sense so I will take the absolute value of sine itself so the range will be [0,width/2]
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
var degrees = 0L
CoroutineScope(context = Dispatchers.Default).launch {
while (true) {
delay(10)
++degrees
++rotationAngle
val angleRadians = Math.toRadians(degrees.toDouble())
val sine = abs(sin(angleRadians)).toFloat()
pulseRadius = width / 2f * sine
invalidate()
}
}
}
here I am calculating the radius and assigning to a global variable then invalidating to draw the circle.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
with(canvas) {
rotate(rotationAngle, midPoint, midPoint)
drawPath(path, alphaPaint)
drawLine(
midPoint,
midPoint,
width.toFloat(),
midPoint,
linePaint)
drawLine(midPoint,
midPoint,
midPoint,
0f,
linePaint)
//DRAW OUR CIRCLE
drawCircle(midPoint, midPoint, pulseRadius, alphaPaint)
}
}

cool now let’s add our jets from the previous article and add the background to create a war zone

if you find it interesting you can find complete code at my github
thanks, have a good day 😊