-
Notifications
You must be signed in to change notification settings - Fork 459
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
[python] Turtle module #748
Conversation
I've added the turtle module to the Python toolbox and some extra functions. It's very usable now, even if without speed-dialing menus like TI-83s it's a bit tedious to select things in the toolbox... I've decided to leave out a lot of things from the standard module in order to keep the implementation simple. Nevertheless, I've kept most short function name substitutes implemented but not shown inside the toolbox in order to (hopefully) increase compatibility a bit with samples on the net for the full-blown CPython turtle module. I've left out a couple of "advanced" functions out of the toolbox to try and keep the toolbox at a reasonable size without confusing novice users. I also added two new Python sample scripts, a spiral and a koch fractal. The turtle implementation could use some comments, but otherwise the code should be in fairly good shape for an initial review (except for the clock bit to control turtle speed). |
Wow, definitely something we need to merge! Thank you very much for this awesome contribution @boricj ! We'll try to review this as soon as possible 😄 |
Thanks @Ecco! Now that I look back on this, I do wonder if improving the Python runtime environment so that we can run the turtle module on top of it would be a better solution. After all, there's really nothing mandating a native implementation over a bytecode MicroPython module here, except for the lack of a decent Python platform API... Besides, calculator game programmers would very much prefer this option for obvious reasons. |
I've translated most of this native implementation to Python and it is indeed a much better solution. There's much less glue to maintain, the turtle implementation is looking far less scary in Python and the ability to interrupt a drawing comes "for free" since we're inside the MicroPython environment. The downsize is that the sheer size of the |
Sorry, but they're really not obvious to me. Would you mind explaining why?
I'd love to see some benchmarks. Not only on speed, but also on memory usage (RAM + Flash). I'd assume the C++ version would win, but who knows 😄 Also, I don't find the implementation scary, and pretty much everything in Epsilon is C++ anyway… |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A small preliminary review 😄
python/port/modturtle_impl.cpp
Outdated
} | ||
|
||
mp_obj_t turtle_goto(size_t n_args, const mp_obj_t *args) { | ||
float newx, newy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use mp_float_t which is a dynamic type. MicroPythons floats can either be single or double precision floats, and in our case they are actually doubles.
float newx, newy; | |
mp_float_t newx, newy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we might want to remove single-precision floats altogether at some point, so unless performance is critical I suggest using doubles when possible.
python/port/modturtle_impl.cpp
Outdated
if (t_dot) { | ||
delete[] t_dot; | ||
} | ||
t_dot = new uint8_t[size*size]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're on a malloc
diet. Please avoid heap allocations.
kandinsky/src/context_rect.cpp
Outdated
@@ -14,7 +14,7 @@ void KDContext::fillRect(KDRect rect, KDColor color) { | |||
} | |||
|
|||
/* Note: we support the case where workingBuffer IS equal to pixels */ | |||
void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer) { | |||
void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer, KDColor * prevPixels) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm rather uncomfortable modifying this, and I also have a hard time figuring out what you're trying to achieve here 😄 Would you mind explaining so we can see if there's a better solution?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to move the turtle across the screen, I need to erase the turtle icon by blitting back the pixels under it, so I need a way to fetch those pixels that's more efficient than brute-forcing getPixel()
. This is a very dirty hack to write-back the pixels under the turtle, but I've changed it to a more conventional fetchPixels(KDRect rect, KDColor * pixels)
in my current Python rewriting (not online yet).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see 😄I think such an API could make sense in KDContext
(a wrapper around pullRect
and absoluteFillRect
).
One upside to a turtle implementation in Python is that the platform API must be improved to accommodate it (more drawing primitives inside
Except for "Scary" is perhaps not the most adequate word here, but having the bulk of the code written in plain Python does result in a much prettier implementation overall and conveniently solves a bunch of issues for free. I'll make a pull request with that version as soon as possible. |
I see. Note that it's something we could do anyway 😄
Indeed, but that would be a rather short story, since there's already a C++ API for this:
There's one specific area I think the C++ version would be better: it could bypass Python-Heap allocation altogether, leaving more heap for end users. |
Unless I'm mistaken, |
Indeed, you're a 100% right! We even have a lengthy comment on the matter 😄 |
The more I'm thinking about this PR, the more I think I'd like the C++ version more than the Python one. It can be a great thing to actually compare both the C++ and the Python version, but all things equal I think I'd be more likely to eventually merge the C++ one: simpler build system, works like everything else, etc… |
I've opened a new pull request with the Python approach (#752). I think I'll close this pull request once feature parity has been reached as the other approach requires far less glue to work. |
It boils down to a matter of personal taste, but I think the native approach requires way too much domain-specific glue. The Python approach does require a bit of work to make the MicroPython cross-compiler fit in the epsilon build system, but it's not that bad and the side-effects are more useful (mechanism for additional bytecode modules in place for the future, generic improvements in kandinsky usable by everyone, keyboard interruption handled for free). The native approach will have the edge on raw performance and memory consumption, but it's far more complicated to get right and it yields no improvements outside of the turtle module itself. Perhaps a hybrid approach (where drawing responsibilities are done by native code but the brain of the turtle is hosted by Python code) could bring the best of the two worlds... |
Wouldn’t you adapt the color if the turtle icon, depending on the active color (just like in the official module, if I’m not wrong) ? Else, I’m not sure the icon currently used have to be changed, if I can give my opinions without having tested it. |
I could, but that's most useful when you have several turtles on screen, which this implementation will not support. The icon does have 8 different directions to represent headings, but the graphics are just placeholders for now subject to change. |
Strangely I get an error while building (similar in both "v1" and v2)
|
Sounds like your compiler handles PODs the old, pre-C++11 way. Ensure |
The drawing was really slowed down by the waiting, and no tearing effect has been noticed without waitForVBlank
These two methods are equal.
The 'blue' command would be stripped and become ''.
To simplify ToolboxMessageTree cosntruction
This way, Turtle::draw() and erase() are more symetrical and no not perform themselves if the turtle is already drawn / erased.
Very cool @boricj ! Looking forward to the release |
This is a native implementation of the classic Python
turtle
module, complete with a cute lil' turtle icon. It's actually a fully native implementation in order to improve performance rendering. This currently adds a little bit over 2 KiB of Flash to the firmware.There are lots of improvements to do before this can be merged:
turtle
moduleFixes #156.