diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index 0b8f85c90..abba04f46 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -177,6 +177,9 @@ void Turtle::setVisible(bool visible) { void Turtle::write(const char * string) { // We erase the turtle to redraw it on top of the text + if (isOutOfBounds()) { + return; + } erase(); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDContext * ctx = KDIonContext::sharedContext(); @@ -195,6 +198,10 @@ void Turtle::viewDidDisappear() { m_drawn = false; } +bool Turtle::isOutOfBounds() const { + return absF(x()) > k_maxPosition || absF(y()) > k_maxPosition; +}; + // Private functions void Turtle::setHeadingPrivate(mp_float_t angle) { @@ -255,6 +262,7 @@ bool Turtle::hasDotBuffers() { } KDRect Turtle::iconRect() const { + assert(!isOutOfBounds()); KDPoint iconOffset = KDPoint(-k_iconSize/2, -k_iconSize/2); return KDRect(position().translatedBy(iconOffset), k_iconSize, k_iconSize); } @@ -262,7 +270,7 @@ KDRect Turtle::iconRect() const { bool Turtle::draw(bool force) { MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); - if ((m_speed > 0 || force) && m_visible && !m_drawn && hasUnderneathPixelBuffer()) { + if ((m_speed > 0 || force) && m_visible && !m_drawn && hasUnderneathPixelBuffer() && !isOutOfBounds()) { KDContext * ctx = KDIonContext::sharedContext(); // Get the pixels underneath the turtle @@ -344,7 +352,7 @@ bool Turtle::dot(mp_float_t x, mp_float_t y) { MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); // Draw the dot if the pen is down - if (m_penDown && hasDotBuffers()) { + if (m_penDown && hasDotBuffers() && !isOutOfBounds()) { KDContext * ctx = KDIonContext::sharedContext(); KDRect rect( position(x, y).translatedBy(KDPoint(-m_penSize/2, -m_penSize/2)), @@ -369,6 +377,7 @@ bool Turtle::dot(mp_float_t x, mp_float_t y) { void Turtle::drawPaw(PawType type, PawPosition pos) { assert(!m_drawn); assert(m_underneathPixelBuffer != nullptr); + assert(!isOutOfBounds()); KDCoordinate pawOffset = 5; constexpr float crawlOffset = 0.6f; constexpr float angles[] = {M_PI_4, 3*M_PI_4, -3*M_PI_4, -M_PI_4}; @@ -391,7 +400,7 @@ void Turtle::drawPaw(PawType type, PawPosition pos) { } void Turtle::erase() { - if (!m_drawn || m_underneathPixelBuffer == nullptr) { + if (!m_drawn || m_underneathPixelBuffer == nullptr || isOutOfBounds()) { return; } KDContext * ctx = KDIonContext::sharedContext(); diff --git a/python/port/mod/turtle/turtle.h b/python/port/mod/turtle/turtle.h index bf0045ee9..e64808396 100644 --- a/python/port/mod/turtle/turtle.h +++ b/python/port/mod/turtle/turtle.h @@ -82,6 +82,13 @@ public: void viewDidDisappear(); + /* isOutOfBounds returns true if nothing should be drawn at current position. + * We avoid drawing at extreme position (far from screen bounds) to prevent + * coordinate overflows. However, this solution makes the turtle go faster + * when out of bound, and can prevent text that would have been visible to be + * drawn. We use very large bounds to temper these effects. */ + bool isOutOfBounds() const; + private: static constexpr mp_float_t k_headingScale = M_PI / 180; /* The Y axis is oriented upwards in Turtle and downwards in Kandinsky, so we @@ -94,6 +101,7 @@ private: static constexpr KDColor k_defaultColor = KDColorBlack; static constexpr uint8_t k_defaultPenSize = 1; static constexpr const KDFont * k_font = KDFont::LargeFont; + static constexpr mp_float_t k_maxPosition = KDCOORDINATE_MAX * 0.75f; enum class PawType : uint8_t { FrontRight = 0,