mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[python/turtle] Use a Turtle class
This commit is contained in:
@@ -13,6 +13,7 @@ public:
|
||||
// Pixel manipulation
|
||||
void setPixel(KDPoint p, KDColor c);
|
||||
KDColor getPixel(KDPoint p);
|
||||
void getPixels(KDRect r, KDColor * pixels);
|
||||
|
||||
// Text
|
||||
KDPoint drawString(const char * text, KDPoint p, const KDFont * font = KDFont::LargeFont, KDColor textColor = KDColorBlack, KDColor backgroundColor = KDColorWhite, int maxLength = -1);
|
||||
@@ -22,7 +23,7 @@ public:
|
||||
|
||||
// Rect
|
||||
void fillRect(KDRect rect, KDColor color);
|
||||
void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer, KDColor * prevPixels = nullptr);
|
||||
void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer);
|
||||
void blendRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer);
|
||||
void strokeRect(KDRect rect, KDColor color);
|
||||
protected:
|
||||
|
||||
@@ -16,3 +16,8 @@ KDColor KDContext::getPixel(KDPoint p) {
|
||||
}
|
||||
return KDColorBlack;
|
||||
}
|
||||
|
||||
void KDContext::getPixels(KDRect r, KDColor * pixels) {
|
||||
KDRect rect = r.translatedBy(m_origin);
|
||||
pullRect(rect, pixels);
|
||||
}
|
||||
|
||||
@@ -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, KDColor * prevPixels) {
|
||||
void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer) {
|
||||
KDRect absoluteRect = absoluteFillRect(rect);
|
||||
|
||||
if (absoluteRect.isEmpty()) {
|
||||
@@ -27,9 +27,6 @@ void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor
|
||||
* continuous area. */
|
||||
|
||||
if (absoluteRect.width() == rect.width() && absoluteRect.height() == rect.height()) {
|
||||
if (prevPixels) {
|
||||
pullRect(absoluteRect, prevPixels);
|
||||
}
|
||||
pushRect(absoluteRect, pixels);
|
||||
return;
|
||||
}
|
||||
@@ -50,9 +47,6 @@ void KDContext::fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor
|
||||
for (KDCoordinate j=0; j<absoluteRect.height(); j++) {
|
||||
KDRect absoluteRow = KDRect(absoluteRect.x(), absoluteRect.y()+j, absoluteRect.width(), 1);
|
||||
KDColor * rowPixels = (KDColor *)pixels+startingI+rect.width()*(startingJ+j);
|
||||
if (prevPixels) {
|
||||
pullRect(absoluteRow, prevPixels + startingI+rect.width()*(startingJ+j));
|
||||
}
|
||||
pushRect(absoluteRow, rowPixels);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -135,6 +135,7 @@ port_objs += $(addprefix python/port/,\
|
||||
mod/time/modtime_table.o \
|
||||
mod/turtle/modturtle.o \
|
||||
mod/turtle/modturtle_table.o \
|
||||
mod/turtle/turtle.o \
|
||||
mphalport.o \
|
||||
)
|
||||
|
||||
@@ -165,7 +166,7 @@ $(py_objs): SFLAGS := $(subst -Os,-O0,$(SFLAGS))
|
||||
endif
|
||||
|
||||
# Icon dependency
|
||||
python/port/mod/turtle/modturtle.cpp: python/port/mod/turtle/turtle_icon.o
|
||||
python/port/mod/turtle/turtle.cpp: python/port/mod/turtle/turtle_icon.o
|
||||
|
||||
# QSTR generation
|
||||
|
||||
|
||||
@@ -1,303 +1,108 @@
|
||||
extern "C" {
|
||||
#include "modturtle.h"
|
||||
}
|
||||
#include "../../helpers.h"
|
||||
#include <kandinsky.h>
|
||||
#include <ion.h>
|
||||
#include <math.h>
|
||||
#include "port.h"
|
||||
#include "turtle_icon.h"
|
||||
#include "turtle.h"
|
||||
|
||||
constexpr mp_float_t t_heading_offset = M_PI_2;
|
||||
constexpr mp_float_t t_heading_scale = M_PI / 180;
|
||||
constexpr int t_x_offset = Ion::Display::Width / 2;
|
||||
constexpr int t_y_offset = (Ion::Display::Height-18) / 2;
|
||||
constexpr int t_size = 9;
|
||||
constexpr int t_icons = 8;
|
||||
constexpr KDPoint t_icon_offset(-t_size/2 + 1, -t_size/2 + 1);
|
||||
constexpr KDSize t_icon_size(t_size, t_size);
|
||||
|
||||
static KDColor t_color;
|
||||
static mp_float_t t_heading;
|
||||
static mp_float_t t_x, t_y;
|
||||
static bool t_penup;
|
||||
static bool t_hidden;
|
||||
static int t_speed;
|
||||
static int t_dotsize;
|
||||
|
||||
static int t_mileage;
|
||||
static bool t_drawn;
|
||||
|
||||
static KDColor *t_underneath;
|
||||
static KDColor *t_icon;
|
||||
static uint8_t *t_dot;
|
||||
|
||||
static KDPoint pos_turtle(mp_float_t x, mp_float_t y) {
|
||||
return KDPoint(round(x + t_x_offset), round(y + t_y_offset));
|
||||
}
|
||||
|
||||
void draw_turtle() {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
|
||||
int frame = (int)((t_heading / (2*M_PI)) * t_icons + 0.5);
|
||||
if (frame < 0) {
|
||||
frame = t_icons - ((-frame) % t_icons) - 1;
|
||||
}
|
||||
else {
|
||||
frame = frame % t_icons;
|
||||
}
|
||||
|
||||
int offset = frame * (t_size*t_size);
|
||||
|
||||
if (!t_hidden) {
|
||||
KDIonContext::sharedContext()->fillRectWithPixels(KDRect(pos_turtle(t_x, t_y).translatedBy(t_icon_offset), t_icon_size), &t_icon[offset], nullptr, t_drawn ? nullptr : t_underneath);
|
||||
t_drawn = true;
|
||||
}
|
||||
|
||||
if (t_mileage > 1000) {
|
||||
if (t_speed > 0) {
|
||||
micropython_port_interruptible_msleep(8 * (8 - t_speed));
|
||||
t_mileage -= 1000;
|
||||
}
|
||||
else {
|
||||
t_mileage = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void erase_turtle() {
|
||||
if (t_drawn) {
|
||||
KDIonContext::sharedContext()->fillRectWithPixels(KDRect(pos_turtle(t_x, t_y).translatedBy(t_icon_offset), t_icon_size), t_underneath, nullptr);
|
||||
t_drawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
void dot_turtle(mp_float_t x, mp_float_t y) {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
|
||||
if (!t_penup) {
|
||||
KDColor colors[t_dotsize*t_dotsize];
|
||||
KDRect rect(pos_turtle(x, y).translatedBy(KDPoint(-t_dotsize/2, -t_dotsize/2)), KDSize(t_dotsize, t_dotsize));
|
||||
KDIonContext::sharedContext()->blendRectWithMask(rect, t_color, t_dot, colors);
|
||||
}
|
||||
|
||||
if (t_speed > 0) {
|
||||
t_mileage += sqrt((x - t_x) * (x - t_x) + (y - t_y) * (y - t_y)) * 1000;
|
||||
}
|
||||
|
||||
micropython_port_should_interrupt();
|
||||
|
||||
t_x = x;
|
||||
t_y = y;
|
||||
}
|
||||
static Turtle sTurtle;
|
||||
|
||||
mp_obj_t modturtle_forward(mp_obj_t px) {
|
||||
mp_float_t x = t_x + mp_obj_get_float(px)*sin(t_heading);
|
||||
mp_float_t y = t_y + mp_obj_get_float(px)*cos(t_heading);
|
||||
modturtle_moveto(x, y);
|
||||
sTurtle.forward(mp_obj_get_float(px));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_backward(mp_obj_t px) {
|
||||
return modturtle_forward(mp_obj_new_float(-mp_obj_get_float(px)));
|
||||
sTurtle.backward(mp_obj_get_float(px));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_right(mp_obj_t angle) {
|
||||
return modturtle_left(mp_obj_new_float(-mp_obj_get_float(angle)));
|
||||
sTurtle.right(mp_obj_get_float(angle));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_left(mp_obj_t angle) {
|
||||
mp_float_t new_angle = ((t_heading - t_heading_offset) + (mp_obj_get_float(angle) * t_heading_scale)) / t_heading_scale;
|
||||
return modturtle_setheading(mp_obj_new_float(new_angle));
|
||||
sTurtle.left(mp_obj_get_float(angle));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_goto(size_t n_args, const mp_obj_t *args) {
|
||||
mp_float_t newx, newy;
|
||||
mp_float_t x = 0;
|
||||
mp_float_t y = 0;
|
||||
|
||||
if (n_args == 1) {
|
||||
mp_obj_t *mp_coords;
|
||||
mp_obj_t * mp_coords;
|
||||
mp_obj_get_array_fixed_n(args[0], 2, &mp_coords);
|
||||
newx = mp_obj_get_float(mp_coords[0]);
|
||||
newy = mp_obj_get_float(mp_coords[1]);
|
||||
x = mp_obj_get_float(mp_coords[0]);
|
||||
y = mp_obj_get_float(mp_coords[1]);
|
||||
}
|
||||
else {
|
||||
newx = mp_obj_get_float(args[0]);
|
||||
newy = mp_obj_get_float(args[1]);
|
||||
x = mp_obj_get_float(args[0]);
|
||||
y = mp_obj_get_float(args[1]);
|
||||
}
|
||||
|
||||
modturtle_moveto(newx, newy);
|
||||
sTurtle.goTo(x, y);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_setheading(mp_obj_t angle) {
|
||||
micropython_port_should_interrupt();
|
||||
|
||||
t_heading = mp_obj_get_float(angle) * t_heading_scale + t_heading_offset;
|
||||
|
||||
Ion::Display::waitForVBlank();
|
||||
erase_turtle();
|
||||
draw_turtle();
|
||||
|
||||
sTurtle.setHeading(mp_obj_get_float(angle));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_speed(mp_obj_t speed) {
|
||||
int new_speed = mp_obj_get_int(speed);
|
||||
|
||||
if (new_speed < 0) {
|
||||
new_speed = 0;
|
||||
}
|
||||
else if (new_speed > 10) {
|
||||
new_speed = 10;
|
||||
}
|
||||
|
||||
t_speed = new_speed;
|
||||
sTurtle.setSpeed(mp_obj_get_int(speed));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_position() {
|
||||
mp_obj_t mp_pos[2];
|
||||
mp_pos[0] = mp_obj_new_float(t_x);
|
||||
mp_pos[1] = mp_obj_new_float(t_y);
|
||||
mp_pos[0] = mp_obj_new_float(sTurtle.x());
|
||||
mp_pos[1] = mp_obj_new_float(sTurtle.y());
|
||||
return mp_obj_new_tuple(2, mp_pos);
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_heading() {
|
||||
return mp_obj_new_float((t_heading - t_heading_offset) / t_heading_scale);
|
||||
return mp_obj_new_float(sTurtle.heading());
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_pendown() {
|
||||
t_penup = false;
|
||||
sTurtle.setPenDown(true);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_penup() {
|
||||
t_penup = true;
|
||||
sTurtle.setPenDown(false);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_pensize(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(t_dotsize);
|
||||
return MP_OBJ_NEW_SMALL_INT(sTurtle.penSize());
|
||||
}
|
||||
|
||||
int size = mp_obj_get_int(args[0]);
|
||||
if (size < 1) {
|
||||
size = 1;
|
||||
}
|
||||
else if (size > 10) {
|
||||
size = 10;
|
||||
}
|
||||
|
||||
modturtle_initpen(size);
|
||||
|
||||
sTurtle.setPenSize(mp_obj_get_int(args[0]));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_isdown() {
|
||||
return t_penup ? mp_const_false : mp_const_true;
|
||||
return sTurtle.isPenDown() ? mp_const_true : mp_const_false;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_color(mp_obj_t r, mp_obj_t g, mp_obj_t b) {
|
||||
t_color = KDColor::RGB888(mp_obj_get_int(r), mp_obj_get_int(g), mp_obj_get_int(b));
|
||||
sTurtle.setColor(KDColor::RGB888(mp_obj_get_int(r), mp_obj_get_int(g), mp_obj_get_int(b)));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_showturtle() {
|
||||
t_hidden = false;
|
||||
draw_turtle();
|
||||
sTurtle.setVisible(true);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_hideturtle() {
|
||||
t_hidden = true;
|
||||
erase_turtle();
|
||||
sTurtle.setVisible(false);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle_isvisible() {
|
||||
return t_hidden ? mp_const_false : mp_const_true;
|
||||
}
|
||||
|
||||
mp_obj_t modturtle___init__() {
|
||||
if (!t_underneath) {
|
||||
t_underneath = new KDColor[t_size * t_size];
|
||||
}
|
||||
if (!t_icon) {
|
||||
t_icon = new KDColor[t_size * t_size];
|
||||
|
||||
Ion::decompress(
|
||||
ImageStore::TurtleIcon->compressedPixelData(),
|
||||
reinterpret_cast<uint8_t *>(t_icon),
|
||||
ImageStore::TurtleIcon->compressedPixelDataSize(),
|
||||
sizeof(KDColor) * t_size * t_size * t_icons
|
||||
);
|
||||
}
|
||||
|
||||
t_color = KDColorBlack;
|
||||
t_heading = t_heading_offset;
|
||||
t_x = t_y = 0;
|
||||
t_penup = false;
|
||||
t_speed = 6;
|
||||
t_mileage = 0;
|
||||
t_hidden = false;
|
||||
|
||||
modturtle_initpen(5);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
void modturtle_initpen(int size) {
|
||||
if (t_dot) {
|
||||
delete[] t_dot;
|
||||
}
|
||||
t_dot = new uint8_t[size*size];
|
||||
t_dotsize = size;
|
||||
|
||||
mp_float_t middle = size / 2;
|
||||
for (int j = 0; j < size; j++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
mp_float_t distance = sqrt((j - middle)*(j - middle) + (i - middle)*(i - middle)) / (middle+1);
|
||||
int value = distance * distance * 255;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
else if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
t_dot[j*size + i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void modturtle_moveto(mp_float_t x, mp_float_t y) {
|
||||
mp_float_t oldx = t_x, oldy = t_y;
|
||||
mp_float_t length = sqrt((x - oldx) * (x - oldx) + (y - oldy) * (y - oldy));
|
||||
|
||||
if (length > 1) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
mp_float_t progress = i / length;
|
||||
|
||||
if (t_speed > 0) {
|
||||
Ion::Display::waitForVBlank();
|
||||
}
|
||||
erase_turtle();
|
||||
dot_turtle(x * progress + oldx * (1 - progress), y * progress + oldy * (1 - progress));
|
||||
draw_turtle();
|
||||
}
|
||||
}
|
||||
|
||||
Ion::Display::waitForVBlank();
|
||||
erase_turtle();
|
||||
dot_turtle(x, y);
|
||||
draw_turtle();
|
||||
}
|
||||
|
||||
void modturtle_deinit() {
|
||||
delete[] t_underneath;
|
||||
t_underneath = nullptr;
|
||||
delete[] t_icon;
|
||||
t_icon = nullptr;
|
||||
delete[] t_dot;
|
||||
t_dot = nullptr;
|
||||
return sTurtle.isVisible() ? mp_const_true : mp_const_false;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,3 @@ mp_obj_t modturtle_color(mp_obj_t r, mp_obj_t g, mp_obj_t b);
|
||||
|
||||
mp_obj_t modturtle_showturtle();
|
||||
mp_obj_t modturtle_hideturtle();
|
||||
|
||||
mp_obj_t modturtle___init__();
|
||||
|
||||
void modturtle_initpen(int size);
|
||||
void modturtle_moveto(float x, float y);
|
||||
void modturtle_deinit();
|
||||
|
||||
@@ -22,11 +22,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_showturtle_obj, modturtle_showturtle)
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_hideturtle_obj, modturtle_hideturtle);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_isvisible_obj, modturtle_isvisible);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle___init___obj, modturtle___init__);
|
||||
|
||||
STATIC const mp_rom_map_elem_t modturtle_module_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_turtle) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___init__), (mp_obj_t)&modturtle___init___obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_forward), (mp_obj_t)&modturtle_forward_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fd), (mp_obj_t)&modturtle_forward_obj },
|
||||
{ MP_ROM_QSTR(MP_QSTR_backward), (mp_obj_t)&modturtle_backward_obj },
|
||||
|
||||
229
python/port/mod/turtle/turtle.cpp
Normal file
229
python/port/mod/turtle/turtle.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "turtle.h"
|
||||
extern "C" {
|
||||
#include <py/misc.h>
|
||||
}
|
||||
#include "../../helpers.h"
|
||||
#include "../../port.h"
|
||||
#include "turtle_icon.h"
|
||||
|
||||
template <typename T> static inline T * allocate(size_t count) {
|
||||
// We forward dynamic allocations to the Python heap
|
||||
return static_cast<T*>(m_malloc(sizeof(T) * count));
|
||||
}
|
||||
|
||||
void Turtle::forward(mp_float_t length) {
|
||||
goTo(
|
||||
m_x + length * sin(m_heading),
|
||||
m_y + length * cos(m_heading)
|
||||
);
|
||||
}
|
||||
|
||||
void Turtle::left(mp_float_t angle) {
|
||||
setHeading(
|
||||
((m_heading - k_headingOffset) + (angle * k_headingScale)) / k_headingScale
|
||||
);
|
||||
}
|
||||
|
||||
void Turtle::goTo(mp_float_t x, mp_float_t y) {
|
||||
mp_float_t oldx = m_x;
|
||||
mp_float_t oldy = m_y;
|
||||
mp_float_t length = sqrt((x - oldx) * (x - oldx) + (y - oldy) * (y - oldy));
|
||||
|
||||
if (length > 1) {
|
||||
// Tweening function
|
||||
for (int i = 0; i < length; i++) {
|
||||
mp_float_t progress = i / length;
|
||||
|
||||
if (m_speed > 0) {
|
||||
Ion::Display::waitForVBlank();
|
||||
}
|
||||
erase();
|
||||
dot(x * progress + oldx * (1 - progress), y * progress + oldy * (1 - progress));
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
Ion::Display::waitForVBlank();
|
||||
erase();
|
||||
dot(x, y);
|
||||
draw();
|
||||
}
|
||||
|
||||
mp_float_t Turtle::heading() const {
|
||||
return (m_heading - k_headingOffset) / k_headingScale;
|
||||
}
|
||||
|
||||
void Turtle::setHeading(mp_float_t angle) {
|
||||
micropython_port_vm_hook_loop();
|
||||
|
||||
m_heading = angle * k_headingScale + k_headingScale;
|
||||
|
||||
Ion::Display::waitForVBlank();
|
||||
erase();
|
||||
draw();
|
||||
}
|
||||
|
||||
void Turtle::setSpeed(mp_int_t speed) {
|
||||
// Speed is clamped between 0 and 10
|
||||
if (speed < 0) {
|
||||
m_speed = 0;
|
||||
} else if (speed > 10) {
|
||||
m_speed = 10;
|
||||
} else {
|
||||
m_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
void Turtle::setPenSize(KDCoordinate penSize) {
|
||||
if (m_penSize == penSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dotMask) {
|
||||
m_free(m_dotMask);
|
||||
m_dotMask = nullptr;
|
||||
}
|
||||
|
||||
if (m_dotWorkingPixelBuffer) {
|
||||
m_free(m_dotWorkingPixelBuffer);
|
||||
m_dotWorkingPixelBuffer = nullptr;
|
||||
}
|
||||
|
||||
m_penSize = penSize;
|
||||
}
|
||||
|
||||
void Turtle::setVisible(bool visible) {
|
||||
m_visible = visible;
|
||||
if (m_visible) {
|
||||
draw();
|
||||
} else {
|
||||
erase();
|
||||
}
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
KDPoint Turtle::position(mp_float_t x, mp_float_t y) const {
|
||||
return KDPoint(round(x + k_xOffset), round(y + k_yOffset));
|
||||
}
|
||||
|
||||
bool Turtle::hasUnderneathPixelBuffer() {
|
||||
if (m_underneathPixelBuffer != nullptr) {
|
||||
return true;
|
||||
}
|
||||
m_underneathPixelBuffer = allocate<KDColor>(k_iconSize.width() * k_iconSize.height());
|
||||
return (m_underneathPixelBuffer != nullptr);
|
||||
}
|
||||
|
||||
bool Turtle::hasDotMask() {
|
||||
if (m_dotMask != nullptr) {
|
||||
return true;
|
||||
}
|
||||
m_dotMask = allocate<uint8_t>(m_penSize * m_penSize);
|
||||
if (m_dotMask == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mp_float_t middle = m_penSize / 2;
|
||||
for (int j = 0; j < m_penSize; j++) {
|
||||
for (int i = 0; i < m_penSize; i++) {
|
||||
mp_float_t distance = sqrt((j - middle)*(j - middle) + (i - middle)*(i - middle)) / (middle+1);
|
||||
int value = distance * distance * 255;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
} else if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
m_dotMask[j*m_penSize+i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Turtle::hasDotBuffers() {
|
||||
if (m_dotWorkingPixelBuffer == nullptr) {
|
||||
m_dotWorkingPixelBuffer = allocate<KDColor>(m_penSize * m_penSize);
|
||||
}
|
||||
return m_dotWorkingPixelBuffer && hasDotMask();
|
||||
}
|
||||
|
||||
const KDColor * Turtle::icon() {
|
||||
if (m_iconsPixels == nullptr) {
|
||||
m_iconsPixels = allocate<KDColor>(k_iconSize.width() * k_iconSize.height() * k_numberOfIcons);
|
||||
if (m_iconsPixels == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ion::decompress(
|
||||
ImageStore::TurtleIcon->compressedPixelData(),
|
||||
reinterpret_cast<uint8_t *>(m_iconsPixels),
|
||||
ImageStore::TurtleIcon->compressedPixelDataSize(),
|
||||
sizeof(KDColor) * k_iconSize.width() * k_iconSize.height() * k_numberOfIcons
|
||||
);
|
||||
}
|
||||
|
||||
int frame = ((m_heading / (2*M_PI)) * k_numberOfIcons + 0.5);
|
||||
if (frame < 0) {
|
||||
frame = k_numberOfIcons - ((-frame) % k_numberOfIcons) - 1;
|
||||
} else {
|
||||
frame = frame % k_numberOfIcons;
|
||||
}
|
||||
int offset = frame * k_iconSize.width() * k_iconSize.height();
|
||||
|
||||
return &m_iconsPixels[offset];
|
||||
}
|
||||
|
||||
void Turtle::draw() {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
|
||||
const KDColor * i = icon();
|
||||
|
||||
if (m_visible && i && hasUnderneathPixelBuffer()) {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
KDRect rect = iconRect();
|
||||
ctx->getPixels(rect, m_underneathPixelBuffer);
|
||||
ctx->fillRectWithPixels(rect, i, nullptr);
|
||||
m_drawn = true;
|
||||
}
|
||||
|
||||
if (m_mileage > 1000) {
|
||||
if (m_speed > 0) {
|
||||
micropython_port_interruptible_msleep(8 * (8 - m_speed));
|
||||
m_mileage -= 1000;
|
||||
} else {
|
||||
m_mileage = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Turtle::erase() {
|
||||
if (!m_drawn || m_underneathPixelBuffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->fillRectWithPixels(iconRect(), m_underneathPixelBuffer, nullptr);
|
||||
m_drawn = false;
|
||||
}
|
||||
|
||||
void Turtle::dot(mp_float_t x, mp_float_t y) {
|
||||
MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox();
|
||||
|
||||
if (m_penDown && hasDotBuffers()) {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
KDRect rect(
|
||||
position(x, y).translatedBy(KDPoint(-m_penSize/2, -m_penSize/2)),
|
||||
KDSize(m_penSize, m_penSize)
|
||||
);
|
||||
ctx->blendRectWithMask(rect, m_color, m_dotMask, m_dotWorkingPixelBuffer);
|
||||
}
|
||||
|
||||
if (m_speed > 0) {
|
||||
m_mileage += sqrt((x - m_x) * (x - m_x) + (y - m_y) * (y - m_y)) * 1000;
|
||||
}
|
||||
|
||||
micropython_port_vm_hook_loop();
|
||||
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
}
|
||||
104
python/port/mod/turtle/turtle.h
Normal file
104
python/port/mod/turtle/turtle.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef PYTHON_PORT_MOD_TURTLE_TURTLE_H
|
||||
#define PYTHON_PORT_MOD_TURTLE_TURTLE_H
|
||||
|
||||
extern "C" {
|
||||
#include <py/mpconfig.h>
|
||||
}
|
||||
#include <ion.h>
|
||||
#include <kandinsky.h>
|
||||
#include <math.h>
|
||||
|
||||
class Turtle {
|
||||
public:
|
||||
constexpr Turtle() :
|
||||
m_x(0),
|
||||
m_y(0),
|
||||
m_heading(k_headingOffset),
|
||||
m_color(KDColorBlack),
|
||||
m_penDown(true),
|
||||
m_visible(true),
|
||||
m_speed(6),
|
||||
m_penSize(5),
|
||||
m_mileage(0),
|
||||
m_drawn(false),
|
||||
m_underneathPixelBuffer(nullptr),
|
||||
m_dotMask(nullptr),
|
||||
m_dotWorkingPixelBuffer(nullptr),
|
||||
m_iconsPixels(nullptr)
|
||||
{}
|
||||
|
||||
void forward(mp_float_t length);
|
||||
void backward(mp_float_t length) { forward(-length); }
|
||||
void right(mp_float_t angle) { left(-angle); }
|
||||
void left(mp_float_t angle);
|
||||
void goTo(mp_float_t x, mp_float_t y);
|
||||
|
||||
mp_float_t heading() const;
|
||||
void setHeading(mp_float_t angle);
|
||||
|
||||
void setSpeed(mp_int_t speed);
|
||||
|
||||
mp_float_t x() const { return m_x; }
|
||||
mp_float_t y() const { return m_y; }
|
||||
|
||||
KDCoordinate penSize() const { return m_penSize; }
|
||||
void setPenSize(KDCoordinate penSize);
|
||||
bool isPenDown() const { return m_penDown; }
|
||||
void setPenDown(bool penDown) { m_penDown = penDown; }
|
||||
|
||||
bool isVisible() const { return m_visible; }
|
||||
void setVisible(bool visible);
|
||||
|
||||
void setColor(KDColor c) {
|
||||
m_color = c;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr mp_float_t k_headingOffset = M_PI_2;
|
||||
static constexpr mp_float_t k_headingScale = M_PI / 180;
|
||||
static constexpr KDCoordinate k_xOffset = Ion::Display::Width / 2;
|
||||
static constexpr KDCoordinate k_yOffset = (Ion::Display::Height - 18) / 2;
|
||||
static constexpr KDSize k_iconSize = KDSize(9, 9);
|
||||
static constexpr int k_numberOfIcons = 8;
|
||||
|
||||
KDPoint position(mp_float_t x, mp_float_t y) const;
|
||||
KDPoint position() const { return position(m_x, m_y); }
|
||||
|
||||
bool hasUnderneathPixelBuffer();
|
||||
bool hasDotMask();
|
||||
bool hasDotBuffers();
|
||||
|
||||
KDRect iconRect() const {
|
||||
KDPoint iconOffset = KDPoint(-k_iconSize.width()/2 + 1, -k_iconSize.height()/2 + 1);
|
||||
return KDRect(position().translatedBy(iconOffset), k_iconSize);
|
||||
}
|
||||
|
||||
const KDColor * icon();
|
||||
|
||||
void draw();
|
||||
void erase();
|
||||
void dot(mp_float_t x, mp_float_t y);
|
||||
|
||||
|
||||
mp_float_t m_x;
|
||||
mp_float_t m_y;
|
||||
mp_float_t m_heading;
|
||||
|
||||
KDColor m_color;
|
||||
bool m_penDown;
|
||||
bool m_visible;
|
||||
|
||||
uint8_t m_speed; // Speed is between 1 and 10
|
||||
KDCoordinate m_penSize;
|
||||
KDCoordinate m_mileage;
|
||||
bool m_drawn;
|
||||
|
||||
KDColor * m_underneathPixelBuffer;
|
||||
uint8_t * m_dotMask;
|
||||
KDColor * m_dotWorkingPixelBuffer;
|
||||
KDColor * m_iconsPixels;
|
||||
|
||||
// KDColor m_pixelBuffer[100];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -17,7 +17,6 @@ extern "C" {
|
||||
#include "py/repl.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "mod/turtle/modturtle.h"
|
||||
#include "mphalport.h"
|
||||
}
|
||||
|
||||
@@ -109,7 +108,6 @@ void MicroPython::init(void * heapStart, void * heapEnd) {
|
||||
}
|
||||
|
||||
void MicroPython::deinit() {
|
||||
modturtle_deinit();
|
||||
mp_deinit();
|
||||
}
|
||||
|
||||
|
||||
@@ -619,7 +619,7 @@ typedef double mp_float_t;
|
||||
|
||||
// Whether to call __init__ when importing builtin modules for the first time
|
||||
#ifndef MICROPY_MODULE_BUILTIN_INIT
|
||||
#define MICROPY_MODULE_BUILTIN_INIT (1)
|
||||
#define MICROPY_MODULE_BUILTIN_INIT (0)
|
||||
#endif
|
||||
|
||||
// Whether module weak links are supported
|
||||
|
||||
Reference in New Issue
Block a user