-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
extmod/framebuf: Add polygon drawing methods. #8987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #8987 +/- ##
==========================================
- Coverage 98.37% 98.36% -0.01%
==========================================
Files 156 156
Lines 20281 20366 +85
==========================================
+ Hits 19951 20033 +82
- Misses 330 333 +3
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
I'll be interested to hear the views of the maintainers on this. I'd like to see further enhancements, notably circles/ellipses in filled and outline form but increased firmware size may be an issue. |
The framebuf extmod is totally optional though right? If you don't have a display or if your display needs are not performance sensitive, just set Or are you thinking there should be even finer-grained controls about what functionality to compile in? Maybe we can add Edit: FWIW the |
I don't have a view on build options. I'm interested in the maintainers' view because I favour additional enhancements and might submit a PR for ellipse if there is an interest. |
That thought had occurred to me. framebuf is useful on small devices with, say, monochrome ssd1306 displays. Flash is actually the constraint here since normally framebuf will be frozen. Guess it just depends on how much additional space the extra features will consume? |
Backporting an enhancement from upstream micropython, see: micropython/micropython#8987
My view is: we have a |
Last year I started writing some stuff for framebuffer graphics, inspired by elements of Peters nanogui . As I felt this was unfinished I did not share it. I first developed it in pure python, with generators. Mathematically it was state of the art, I would say, using Besenham algorithms from https://zingl.github.io/Bresenham.pdf. Later I did a conversion of this code to viper. I observed considerable speedups, described in the discussion with peter:
As you see it is now 99 % C-like: Conversion to C should be straightforward. |
Given that a circle is a special case of an ellipse, is there a need for a circle routine? |
If we have a ellipse/arc routine that can draw circles in an obvious way, then that would be a good general purpose function to have. Please submit a PR if you have something @peterhinch |
The viper ellipse routine in the above mentioned vfb_graphics.zip is only slightly different from the circle routine presented and thus should be preferred for transformation to C code for reducing code size, I agree. If you @peterhinch have something already finished or a different approach I would love to test and compare. |
I have nothing ready - for my GUI's I do circle drawing in Python. I'll tackle an ellipse method in C and submit a PR in due course. |
This is my ellipse function converted to C, included is a wrapper so that it compiles to text graphics: Easy to try out.
Should be interesting to see something more concise. |
I think we should discuss circle/ellipse drawing algorithms elsewhere - perhaps in an RFC. To return to this PR I think it should be evaluated (and hopefully merged) on its merits, regardless of future I have tested the PR on an RP2 with ili9341 display and it works well. I have an animation which slowly rotates a filled re-entrant shape. In the past I've seen rasterisation algorithms croak under these circumstances. This works fine. My only question for the team is over the API. The use of a list of lists is both Pythonic and intuitive but it precludes the use of pre-allocated storage. An alternative might be to use an array (perhaps of half-words) holding |
+1. A separate PR with an initial implementation would suffice.
You can preallocate lists (of lists). You could also use tuples (of tuples). If the entries are small ints (which they most likely will be) then it's about the same memory use as an array of ints. But I do agree that an array would be more memory efficient if it uses half words (or bytes for small shapes). It's not that unfriendly, eg: Regarding the name And maybe putting the x/y first in the arg list also makes it more consistent with the other drawing methods? |
I have done some further testing trying to fill ridiculous objects such as
In that case I favour the list of lists on grounds of user friendliness. I was envisaging applications that allocated storage which was then used to render successive objects having the same number of vertices. For example the same shape transformed by scaling or rotation. |
@peterhinch Thanks for testing the code, I also tried to capture some edge cases in the included tests, such as self-intersecting polygons and some degenerate cases.
@dpgeorge I made both of these suggested changes in the latest revision of the patchset -- thanks for the feebback :-) |
Actually this is not true: the current list-of-lists approach requires quite a lot more memory than an array. Eg I think it would be good to support an array with the coordinates. The question is whether it's easy to support a list-of-lists and an array without much increase in code size? Then the user can choose the best approach for their case (list or array). |
I will investigate supporting both ways when I get some time later in the day. Thanks again for the review |
You could have a helper function like (schematically) |
Add methods for drawing filled, and non-filled, polygons. poly(x, y, coords, col) * Uses the existing line-drawing code to render arbitrary polygons using the given coords list, at the given x,y position, in the given colour fill_poly(x, y, coords, col) * Render filled arbitrary, closed, polygons using a fast point-in-polygon algorithm to determine where the edges of the polygon lie on each pixel row Tests and documentation updates are also included. Signed-off-by: Mat Booth <mat.booth@gmail.com>
Hi @dpgeorge, it turned out not to be too difficult to support both ways so I did that, along with extra test cases to cover it and extra detail in the documentation. Please consider re-reviewing the latest revision of the patchset. |
Backporting an enhancement from upstream micropython, see: micropython/micropython#8987
Backporting an enhancement from upstream micropython, see: micropython/micropython#8987
Thank you @mbooth101 for working on this! |
Superceded by #9045 |
The guard against running when flash is disabled isn't perfect and may lead to calling the row handler anyway. We were crashing due to mp_hal_delay_us being in flash. So, move it to RAM. Also move the panic handler to IRAM so we get nice output when this happens. Turn off JTAG by default so its pins can be used by code.py. Fixes micropython#8987
Add methods for drawing filled, and non-filled, polygons.
poly(x, y, coords, col)
the given coords list, at the given x,y position, in the given colour
fill_poly(x, y, coords, col)
algorithm to determine where the edges of the polygon lie on each
pixel row
Tests and documentation updates are also included.
FWIW in my testing on esp32 calling
poly()
to draw non-filled triangles (three-sided convex polygons) is about 15% faster than making 3 distinct calls toline()
You can see
fill_poly()
in action in my experimental 3D renderer for the EMFCamp Tidal esp32 device:tidal3d_demo.mp4