Raspberry Pi, Python and TFT 2.4″ – circle, arc and filling an area

We can draw lines and rectangles so next step should be filling rectangle areas. We will write a function to fill rectangle defined by given points. This will give us some info about performance.
And we will add drawing a circle and an arc.

Source @ GitHub

Fill a rectangle

Knowing what we know, writing fill function is easy:

    def fill_rect(self, x1, y1, x2, y2):
        """fill an area"""
        size = abs(x2 - x1) * abs(y2 - y1)
        self._set_area(
            min(x1, x2),
            min(y1, y2),
            max(x1, x2),
            max(y1, y2)
        )
        GPIO.output(self.pins['RS'], 1)
        color = self._bcolor()
        for _ in range(0, size):
            self.send(color)

We pass start and end coordinates. Next, we select an active area, calculate the number of pixels in it and write a background color this many times.

In main.py we put the code to see how it works:

s.bcolor = {'R': 255, 'G': 0, 'B': 0}
s.fill_rect(10, 10, 50, 50)

s.bcolor = {'R': 0, 'G': 255, 'B': 0}
s.fill_rect(230, 50, 190, 10)

Fill the whole screen

s.bcolor = {'R': 0, 'G': 0, 'B': 255}
s.fill_rect(0, 0, 239, 319)

And… big disappointment.. unwelcome but expected. It is very slow! How slow?

start = time.time()
s.fill_rect(0, 0, 240, 320)
stop = time.time()
print(stop - start)
5.65244913101
pi@raspberrypi2:~/workspace/doton $

Err lets try a different way:

pi@raspberrypi2:~/workspace/doton $ time python main.py

real    0m6.297s
user    0m5.910s
sys     0m0.010s
pi@raspberrypi2:~/workspace/doton $

Nooo…
What can we do to increase speed? We have some options. After we separate driver logic from class logic we can implement a driver in any language: C, asm, PHP, JavaScript :D. Now we are using Python’s module RPi.GPIO.
Another way of improving performance can be done by not refreshing a whole screen but only small areas. It requires more work but may be a good idea.

Draw a circle

We know that performance is not good but this won’t stop us from going further. Let write a function for drawing a circle. We gonna use a midpoint circle algorithm, read more on Wikipedia.

    def draw_circle(self, x, y, radius):
        """draw a circle"""
        err = 0
        offset_x = radius
        offset_y = 0
        while offset_x >= offset_y:
            self.draw_pixel(x + offset_x, y + offset_y)
            self.draw_pixel(x + offset_y, y + offset_x)
            self.draw_pixel(x - offset_y, y + offset_x)
            self.draw_pixel(x - offset_x, y + offset_y)
            self.draw_pixel(x - offset_x, y - offset_y)
            self.draw_pixel(x - offset_y, y - offset_x)
            self.draw_pixel(x + offset_y, y - offset_x)
            self.draw_pixel(x + offset_x, y - offset_y)
            if err <= 0:
                offset_y += 1
                err += 2*offset_y + 1
            else:
                offset_x -= 1
                err -= 2*offset_x + 1

Sadly I do not know of a hack to improve it, something like we done with lines.

Draw an arc

It should be easy, we add start angle and end angle in degree and few ifs and it is done.
Yeh right..A few hours later and I’m on good track. I had to remember all about cos, sin, radians, points and Cartesian coordinate system.
As a base, we are using draw_circle algorithm. It should work with a little bit of tweaking 🙂 I choose a strategy where we recalculate start and end from degree to radians and for each draw_pixel call, app checks if a pixel is between start-end angle.
How?
We know where the pixel should be drawn (or in fact 8 pixels, we are drawing as many in one loop iteration), from (x,y) we calculate angle in radians and compare it with start/end angle. If it is between start and end, draw the pixel.

Conversion from coordinates to radians is done via an atan2 function. But it returns value from -π to π (-180° to 180°) and we require 0 to 2π (0° to 360°). A quick look at how atan2 works, it returns 0 – π for a point in a first half(angle 0° – 180°), and -π – 0 for a second half(180° – 360°).
We do not need to touch values in first half and for the second half, we add 2π to result.

The function took it’s very long time… but we have it:

    def draw_arc(self, x, y, radius, start, end):
        """draw an arc"""
        start = start * math.pi / 180
        end = end * math.pi / 180

        err = 0
        offset_x = radius
        offset_y = 0
        while offset_x >= offset_y:
            if start <= math.atan2(offset_y, offset_x) <= end:
                self.draw_pixel(x + offset_x, y + offset_y)
            if start <= math.atan2(offset_x, offset_y) <= end:
                self.draw_pixel(x + offset_y, y + offset_x)
            if start <= math.atan2(offset_x, -offset_y) <= end:
                self.draw_pixel(x - offset_y, y + offset_x)
            if start <= math.atan2(offset_y, -offset_x) <= end:
                self.draw_pixel(x - offset_x, y + offset_y)

            if start <= math.atan2(-offset_y, -offset_x) + 2*math.pi <= end:
                self.draw_pixel(x - offset_x, y - offset_y)
            if start <= math.atan2(-offset_x, -offset_y) + 2*math.pi <= end:
                self.draw_pixel(x - offset_y, y - offset_x)
            if start <= math.atan2(-offset_x, offset_y) + 2*math.pi <= end:
                self.draw_pixel(x + offset_y, y - offset_x)
            if start <= math.atan2(-offset_y, offset_x) + 2*math.pi <= end:
                self.draw_pixel(x + offset_x, y - offset_y)

            if err <= 0:
                offset_y += 1
                err += 2*offset_y + 1
            else:
                offset_x -= 1
                err -= 2*offset_x + 1

Hurray, we have a nice arc function!

Summary

We learnt that our connection is slow. Very slow. It is something that gonna bug us all the time:/
And from good side, we can fill the area, draw the circle and the arc 🙂

Advertisements

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s