From cc7219b44af8e0f1db0c84d9af38c261402eeeb7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 16 Sep 2022 15:19:20 +0100 Subject: [PATCH] PicoGraphics: Experimental Pretty Poly bring-up. --- libraries/pico_graphics/pico_graphics.cpp | 48 ++++ libraries/pico_graphics/pico_graphics.hpp | 6 + libraries/pico_graphics/pretty-poly-types.hpp | 129 +++++++++ libraries/pico_graphics/pretty-poly.hpp | 250 ++++++++++++++++++ .../modules/picographics/picographics.c | 2 + .../modules/picographics/picographics.cpp | 69 ++++- .../modules/picographics/picographics.h | 1 + 7 files changed, 498 insertions(+), 7 deletions(-) create mode 100644 libraries/pico_graphics/pretty-poly-types.hpp create mode 100644 libraries/pico_graphics/pretty-poly.hpp diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 557fc785..7a4d8ad0 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -1,4 +1,5 @@ #include "pico_graphics.hpp" +#include "pretty-poly.hpp" namespace pimoroni { @@ -16,6 +17,13 @@ namespace pimoroni { int PicoGraphics::get_palette_size() {return 0;} RGB* PicoGraphics::get_palette() {return nullptr;} + size_t PicoGraphics::pretty_poly_buffer_size() { + return pretty_poly::buffer_size(); + } + + void PicoGraphics::pretty_poly_init(void *mem) { + pretty_poly::init(mem); + } void PicoGraphics::set_dimensions(int width, int height) { bounds = clip = {0, 0, width, height}; @@ -241,7 +249,46 @@ namespace pimoroni { } } + void PicoGraphics::polygon(std::vector> contours) { + set_options([this](const pretty_poly::tile_t &tile) -> void { + for(auto y = 0; y < tile.bounds.h; y++) { + for(auto x = 0; x < tile.bounds.w; x++) { + uint8_t alpha = tile.get_value(x, y); + if (alpha > 0) { + pixel({x + tile.bounds.x, y + tile.bounds.y}); + } + } + } + }, pretty_poly::NONE, {0, 0, bounds.w, bounds.h}); + + pretty_poly::draw_polygon(contours); + } + void PicoGraphics::polygon(const std::vector &points) { + + pretty_poly::contour_t contour(new pretty_poly::point_t[points.size()], points.size()); + + for(auto i = 0; i < points.size(); i++) { + contour.points[i] = pretty_poly::point_t(points[i].x, points[i].y); + } + + set_options([this](const pretty_poly::tile_t &tile) -> void { + for(auto y = 0; y < tile.bounds.h; y++) { + for(auto x = 0; x < tile.bounds.w; x++) { + uint8_t alpha = tile.get_value(x, y); + if (alpha > 0) { + pixel({x + tile.bounds.x, y + tile.bounds.y}); + } + } + } + }, pretty_poly::NONE, {0, 0, bounds.w, bounds.h}); + + std::vector> contours; + contours.push_back(contour); + + pretty_poly::draw_polygon(contours); + + /* static int32_t nodes[64]; // maximum allowed number of nodes per scanline for polygon rendering int32_t miny = points[0].y, maxy = points[0].y; @@ -285,6 +332,7 @@ namespace pimoroni { pixel_span(Point(nodes[i], p.y), nodes[i + 1] - nodes[i] + 1); } } + */ } void PicoGraphics::thick_line(Point p1, Point p2, uint thickness) { diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 2dd7c83d..758736ed 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -17,6 +17,8 @@ #include "common/pimoroni_common.hpp" +#include "pretty-poly-types.hpp" + // A tiny graphics library for our Pico products // supports: // - 16-bit (565) RGB @@ -260,6 +262,9 @@ namespace pimoroni { set_font(&font6); }; + size_t pretty_poly_buffer_size(); + void pretty_poly_init(void *mem); + virtual void set_pen(uint c) = 0; virtual void set_pen(uint8_t r, uint8_t g, uint8_t b) = 0; virtual void set_pixel(const Point &p) = 0; @@ -301,6 +306,7 @@ namespace pimoroni { void text(const std::string_view &t, const Point &p, int32_t wrap, float s = 2.0f, float a = 0.0f, uint8_t letter_spacing = 1, bool fixed_width = false); int32_t measure_text(const std::string_view &t, float s = 2.0f, uint8_t letter_spacing = 1, bool fixed_width = false); void polygon(const std::vector &points); + void polygon(std::vector> contours); void triangle(Point p1, Point p2, Point p3); void line(Point p1, Point p2); void thick_line(Point p1, Point p2, uint thickness); diff --git a/libraries/pico_graphics/pretty-poly-types.hpp b/libraries/pico_graphics/pretty-poly-types.hpp new file mode 100644 index 00000000..6b267afb --- /dev/null +++ b/libraries/pico_graphics/pretty-poly-types.hpp @@ -0,0 +1,129 @@ +#pragma once +#include +#include + +using namespace std; + +#ifdef PP_DEBUG +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) +#endif + +namespace pretty_poly { + + enum antialias_t {NONE = 0, X4 = 1, X16 = 2}; + + // 3x3 matrix for coordinate transformations + struct mat3_t { + float v00, v10, v20, v01, v11, v21, v02, v12, v22 = 0.0f; + mat3_t() = default; + mat3_t(const mat3_t &m) = default; + inline mat3_t& operator*= (const mat3_t &m) { + float r00 = this->v00 * m.v00 + this->v01 * m.v10 + this->v02 * m.v20; + float r01 = this->v00 * m.v01 + this->v01 * m.v11 + this->v02 * m.v21; + float r02 = this->v00 * m.v02 + this->v01 * m.v12 + this->v02 * m.v22; + float r10 = this->v10 * m.v00 + this->v11 * m.v10 + this->v12 * m.v20; + float r11 = this->v10 * m.v01 + this->v11 * m.v11 + this->v12 * m.v21; + float r12 = this->v10 * m.v02 + this->v11 * m.v12 + this->v12 * m.v22; + float r20 = this->v20 * m.v00 + this->v21 * m.v10 + this->v22 * m.v20; + float r21 = this->v20 * m.v01 + this->v21 * m.v11 + this->v22 * m.v21; + float r22 = this->v20 * m.v02 + this->v21 * m.v12 + this->v22 * m.v22; + this->v00 = r00; this->v01 = r01; this->v02 = r02; + this->v10 = r10; this->v11 = r11; this->v12 = r12; + this->v20 = r20; this->v21 = r21; this->v22 = r22; + return *this; + } + + static mat3_t identity() {mat3_t m; m.v00 = m.v11 = m.v22 = 1.0f; return m;} + static mat3_t rotation(float a) { + float c = cosf(a), s = sinf(a); mat3_t r = mat3_t::identity(); + r.v00 = c; r.v01 = s; r.v10 = -s; r.v11 = c; return r;} + static mat3_t translation(float x, float y) { + mat3_t r = mat3_t::identity(); r.v02 = x; r.v12 = y; return r;} + static mat3_t scale(float x, float y) { + mat3_t r = mat3_t::identity(); r.v00 = x; r.v11 = y; return r;} + }; + + // point type for contour points + template + struct __attribute__ ((packed)) point_t { + T x, y; + point_t(T x, T y) : x(x), y(y) {} + point_t() : x(0), y(0) {} + inline point_t& operator-= (const point_t &a) {x -= a.x; y -= a.y; return *this;} + inline point_t& operator+= (const point_t &a) {x += a.x; y += a.y; return *this;} + inline point_t& operator*= (const float a) {x *= a; y *= a; return *this;} + inline point_t& operator*= (const mat3_t &a) {this->transform(a); return *this;} + inline point_t& operator/= (const float a) {x /= a; y /= a; return *this;} + inline point_t& operator/= (const point_t &a) {x /= a.x; y /= a.y; return *this;} + void transform(const mat3_t &m) { + this->x = (m.v00 * float(this->x) + m.v01 * float(this->y) + m.v02); + this->y = (m.v10 * float(this->x) + m.v11 * float(this->y) + m.v12); + } + + }; + + template inline point_t operator- (point_t lhs, const point_t &rhs) { lhs -= rhs; return lhs; } + template inline point_t operator- (const point_t &rhs) { return point_t(-rhs.x, -rhs.y); } + template inline point_t operator+ (point_t lhs, const point_t &rhs) { lhs += rhs; return lhs; } + template inline point_t operator* (point_t lhs, const float rhs) { lhs *= rhs; return lhs; } + template inline point_t operator* (point_t lhs, const point_t &rhs) { lhs *= rhs; return lhs; } + template inline point_t operator* (point_t lhs, const mat3_t &rhs) { lhs *= rhs; return lhs; } + template inline point_t operator/ (point_t lhs, const float rhs) { lhs /= rhs; return lhs; } + template inline point_t operator/ (point_t lhs, const point_t &rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; } + + + // rect type for bounds and clipping rectangles + struct rect_t { + int x, y, w, h; + rect_t() : x(0), y(0), w(0), h(0) {} + rect_t(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {} + bool empty() const {return this->w == 0 && this->h == 0;} + rect_t intersection(const rect_t &c) { + return rect_t(max(this->x, c.x), max(this->y, c.y), + max(0, min(this->x + this->w, c.x + c.w) - max(this->x, c.x)), + max(0, min(this->y + this->h, c.y + c.h) - max(this->y, c.y))); + } + rect_t merge(const rect_t &c) { + return rect_t(min(this->x, c.x), min(this->y, c.y), + max(this->x + this->w, c.x + c.w) - min(this->x, c.x), + max(this->y + this->h, c.y + c.h) - min(this->y, c.y)); + } + }; + + struct tile_t { + rect_t bounds; + unsigned stride; + uint8_t *data; + + tile_t() {}; + + int get_value(int x, int y) const { + return this->data[x + y * this->stride]; + } + }; + + template + struct contour_t { + point_t *points; + unsigned count; + + contour_t() {} + contour_t(vector> v) : points(v.data()), count(v.size()) {}; + contour_t(point_t *points, unsigned count) : points(points), count(count) {}; + + rect_t bounds() { + T minx = this->points[0].x, maxx = minx; + T miny = this->points[0].y, maxy = miny; + for(auto i = 1; i < this->count; i++) { + minx = min(minx, this->points[i].x); + miny = min(miny, this->points[i].y); + maxx = max(maxx, this->points[i].x); + maxy = max(maxy, this->points[i].y); + } + return rect_t(minx, miny, maxx - minx, maxy - miny); + } + }; + +} diff --git a/libraries/pico_graphics/pretty-poly.hpp b/libraries/pico_graphics/pretty-poly.hpp new file mode 100644 index 00000000..14e0488b --- /dev/null +++ b/libraries/pico_graphics/pretty-poly.hpp @@ -0,0 +1,250 @@ +#include +#include +#include +#include +#include + + + +using namespace std; + +#ifdef PP_DEBUG +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) +#endif + +namespace pretty_poly { + + //typedef void (*tile_callback_t)(const tile_t &tile); + + typedef std::function tile_callback_t; + + // buffer that each tile is rendered into before callback + constexpr unsigned tile_buffer_size = 1024; + //uint8_t tile_buffer[tile_buffer_size]; + uint8_t *tile_buffer; + + // polygon node buffer handles at most 16 line intersections per scanline + // is this enough for cjk/emoji? (requires a 2kB buffer) + constexpr unsigned node_buffer_size = 32; + + //int nodes[node_buffer_size][32]; + //unsigned node_counts[node_buffer_size]; + + int (*nodes)[32]; + unsigned *node_counts; + + // default tile bounds to X1 antialiasing + rect_t tile_bounds(0, 0, tile_buffer_size / node_buffer_size, node_buffer_size); + + // user settings + namespace settings { + rect_t clip(0, 0, 320, 240); + tile_callback_t callback; + antialias_t antialias = antialias_t::NONE; + } + + size_t buffer_size() { + return tile_buffer_size + (node_buffer_size * sizeof(unsigned)) + (node_buffer_size * 32 * sizeof(int)); + } + + void init(void *memory) { + tile_buffer = new(memory) uint8_t[tile_buffer_size]; + node_counts = new(memory + tile_buffer_size) unsigned[node_buffer_size]; + nodes = new(memory + tile_buffer_size + (node_buffer_size * sizeof(unsigned))) int[node_buffer_size][32]; + } + + void set_options(tile_callback_t callback, antialias_t antialias, rect_t clip) { + settings::callback = callback; + settings::antialias = antialias; + settings::clip = clip; + + // recalculate the tile size for rendering based on antialiasing level + int tile_height = node_buffer_size >> antialias; + tile_bounds = rect_t(0, 0, tile_buffer_size / tile_height, tile_height); + } + + // dy step (returns 1, 0, or -1 if the supplied value is > 0, == 0, < 0) + __attribute__((always_inline)) int sign(int v) { + // assumes 32-bit int/unsigned + return ((unsigned)-v >> 31) - ((unsigned)v >> 31); + } + + // write out the tile bits + void debug_tile(const tile_t &tile) { + debug(" - tile %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h); + for(auto y = 0; y < tile.bounds.h; y++) { + debug("[%3d]: ", y); + for(auto x = 0; x < tile.bounds.w; x++) { + debug("%d", tile.get_value(x, y)); + } + debug("\n"); + } + debug("-----------------------\n"); + } + + void add_line_segment_to_nodes(const point_t &start, const point_t &end) { + // swap endpoints if line "pointing up", we do this because we + // alway skip the last scanline (so that polygons can but cleanly + // up against each other without overlap) + int sx = start.x, sy = start.y, ex = end.x, ey = end.y; + + if(ey < sy) { + swap(sy, ey); + swap(sx, ex); + } + + /*sx <<= settings::antialias; + ex <<= settings::antialias; + sy <<= settings::antialias; + ey <<= settings::antialias;*/ + + int x = sx; + int y = sy; + int e = 0; + + int xinc = sign(ex - sx); + int einc = abs(ex - sx) + 1; + + // todo: preclamp sy and ey (and therefore count) no need to perform + // that test inside the loop + int dy = ey - sy; + int count = dy; + debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey); + // loop over scanlines + while(count--) { + // consume accumulated error + while(e > dy) {e -= dy; x += xinc;} + + if(y >= 0 && y < node_buffer_size) { + // clamp node x value to tile bounds + int nx = max(min(x, (int)(tile_bounds.w << settings::antialias)), 0); + debug(" + adding node at %d, %d\n", x, y); + // add node to node list + nodes[y][node_counts[y]++] = nx; + } + + // step to next scanline and accumulate error + y++; + e += einc; + } + } + + template + void build_nodes(const contour_t &contour, const tile_t &tile, point_t origin = point_t(0, 0), int scale = 65536) { + int ox = (origin.x - tile.bounds.x) << settings::antialias; + int oy = (origin.y - tile.bounds.y) << settings::antialias; + + // start with the last point to close the loop + point_t last( + (((contour.points[contour.count - 1].x * scale) << settings::antialias) / 65536) + ox, + (((contour.points[contour.count - 1].y * scale) << settings::antialias) / 65536) + oy + ); + + for(int i = 0; i < contour.count; i++) { + point_t point( + (((contour.points[i].x * scale) << settings::antialias) / 65536) + ox, + (((contour.points[i].y * scale) << settings::antialias) / 65536) + oy + ); + + add_line_segment_to_nodes(last, point); + + last = point; + } + } + + void render_nodes(const tile_t &tile) { + for(auto y = 0; y < node_buffer_size; y++) { + if(node_counts[y] == 0) { + continue; + } + + std::sort(&nodes[y][0], &nodes[y][0] + node_counts[y]); + + for(auto i = 0; i < node_counts[y]; i += 2) { + int sx = nodes[y][i + 0]; + int ex = nodes[y][i + 1]; + + if(sx == ex) { + continue; + } + + debug(" - render span at %d from %d to %d\n", y, sx, ex); + + for(int x = sx; x < ex; x++) { + tile.data[(x >> settings::antialias) + (y >> settings::antialias) * tile.stride]++; + } + } + } + } + + template + void draw_polygon(T *points, unsigned count) { + std::vector> contours; + contour_t c(points, count); + contours.push_back(c); + draw_polygon(contours); + } + + template + void draw_polygon(std::vector> contours, point_t origin = point_t(0, 0), int scale = 65536) { + + debug("> draw polygon with %lu contours\n", contours.size()); + + if(contours.size() == 0) { + return; + } + + // determine extreme bounds + rect_t polygon_bounds = contours[0].bounds(); + for(auto &contour : contours) { + polygon_bounds = polygon_bounds.merge(contour.bounds()); + } + + polygon_bounds.x = ((polygon_bounds.x * scale) / 65536) + origin.x; + polygon_bounds.y = ((polygon_bounds.y * scale) / 65536) + origin.y; + polygon_bounds.w = ((polygon_bounds.w * scale) / 65536); + polygon_bounds.h = ((polygon_bounds.h * scale) / 65536); + + debug(" - bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h); + debug(" - clip %d, %d (%d x %d)\n", settings::clip.x, settings::clip.y, settings::clip.w, settings::clip.h); + + + memset(nodes, 0, node_buffer_size * sizeof(unsigned) * 32); + + // iterate over tiles + debug(" - processing tiles\n"); + for(auto y = polygon_bounds.y; y < polygon_bounds.y + polygon_bounds.h; y += tile_bounds.h) { + for(auto x = polygon_bounds.x; x < polygon_bounds.x + polygon_bounds.w; x += tile_bounds.w) { + tile_t tile; + tile.bounds = rect_t(x, y, tile_bounds.w, tile_bounds.h).intersection(settings::clip); + tile.stride = tile_bounds.w; + tile.data = tile_buffer; + debug(" : %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h); + + // if no intersection then skip tile + if(tile.bounds.empty()) { + debug(" : empty when clipped, skipping\n"); + continue; + } + + // clear existing tile data and nodes + memset(node_counts, 0, node_buffer_size * sizeof(unsigned)); + memset(tile.data, 0, tile_buffer_size); + + // build the nodes for each contour + for(contour_t &contour : contours) { + debug(" : build nodes for contour\n"); + build_nodes(contour, tile, origin, scale); + } + + debug(" : render the tile\n"); + // render the tile + render_nodes(tile); + + settings::callback(tile); + } + } + } +} diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index 01c58763..e223b851 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -36,6 +36,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_character_obj, 1, ModPicoGraphics_cha MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_text_obj, 1, ModPicoGraphics_text); MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_measure_text_obj, 1, ModPicoGraphics_measure_text); MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_polygon_obj, 2, ModPicoGraphics_polygon); +MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_pretty_polygon_obj, 2, ModPicoGraphics_pretty_polygon); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_triangle_obj, 7, 7, ModPicoGraphics_triangle); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_line_obj, 5, 6, ModPicoGraphics_line); @@ -72,6 +73,7 @@ STATIC const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&ModPicoGraphics_text_obj) }, { MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&ModPicoGraphics_measure_text_obj) }, { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&ModPicoGraphics_polygon_obj) }, + { MP_ROM_QSTR(MP_QSTR_pretty_polygon), MP_ROM_PTR(&ModPicoGraphics_pretty_polygon_obj) }, { MP_ROM_QSTR(MP_QSTR_triangle), MP_ROM_PTR(&ModPicoGraphics_triangle_obj) }, { MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&ModPicoGraphics_line_obj) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 56dde371..d94b2086 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -7,6 +7,7 @@ #include "drivers/inky73/inky73.hpp" #include "drivers/psram_display/psram_display.hpp" #include "libraries/pico_graphics/pico_graphics.hpp" +#include "libraries/pico_graphics/pretty-poly-types.hpp" #include "common/pimoroni_common.hpp" #include "common/pimoroni_bus.hpp" #include "common/pimoroni_i2c.hpp" @@ -298,17 +299,17 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size pimoroni::ParallelPins parallel_bus = {10, 11, 12, 13, 14, 2}; // Default for Tufty 2040 parallel pimoroni::I2C *i2c_bus = nullptr; - if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) { + if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &SPIPins_type)) { if(bus_type != BUS_SPI) mp_raise_ValueError("unexpected SPI bus!"); _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); spi_bus = *(SPIPins *)(bus->pins); - } else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) { + } else if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &ParallelPins_type)) { if(bus_type != BUS_PARALLEL) mp_raise_ValueError("unexpected Parallel bus!"); _PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj); parallel_bus = *(ParallelPins *)(bus->pins); - } else if (mp_obj_is_type(args[ARG_bus].u_obj, &PimoroniI2C_type) || MP_OBJ_IS_TYPE(args[ARG_bus].u_obj, &machine_i2c_type)) { + } else if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &PimoroniI2C_type) || mp_obj_is_exact_type(args[ARG_bus].u_obj, &machine_hw_i2c_type)) { if(bus_type != BUS_I2C) mp_raise_ValueError("unexpected I2C bus!"); self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_bus].u_obj); i2c_bus = (pimoroni::I2C *)(self->i2c->i2c); @@ -442,6 +443,8 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size self->display->update(self->graphics); } + self->graphics->pretty_poly_init(m_tracked_calloc(self->graphics->pretty_poly_buffer_size(), sizeof(uint8_t))); + return MP_OBJ_FROM_PTR(self); } @@ -807,7 +810,7 @@ mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp // Check if there is only one argument, which might be a list if(n_args == 2) { - if(mp_obj_is_type(pos_args[1], &mp_type_list)) { + if(mp_obj_is_exact_type(pos_args[1], &mp_type_list)) { mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); if(points->len <= 0) mp_raise_ValueError("set_palette(): cannot provide an empty list"); @@ -822,7 +825,7 @@ mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp for(size_t i = 0; i < num_tuples; i++) { mp_obj_t obj = tuples[i]; - if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple"); + if(!mp_obj_is_exact_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple"); mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); @@ -1019,6 +1022,58 @@ mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, m return mp_obj_new_int(width); } +/* +pretty_polygon(( + (0, 0), + (0, 0) +), ( + +), ( + +)) +*/ + +mp_obj_t ModPicoGraphics_pretty_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + size_t num_tuples = n_args - 1; + const mp_obj_t *lists = pos_args + 1; + + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], ModPicoGraphics_obj_t); + + std::vector> contours; + + for(auto i = 0; i < num_tuples; i++) { + mp_obj_t c_obj = lists[i]; + + if(!mp_obj_is_exact_type(c_obj, &mp_type_list)) mp_raise_ValueError("Not a list"); + + mp_obj_list_t *t_contour = MP_OBJ_TO_PTR2(c_obj, mp_obj_list_t); + + pretty_poly::point_t *points = new pretty_poly::point_t[t_contour->len]; + + for(auto p = 0; p < t_contour->len; p++) { + mp_obj_t p_obj = t_contour->items[p]; + + if(!mp_obj_is_exact_type(p_obj, &mp_type_tuple)) mp_raise_ValueError("Not a tuple"); + + mp_obj_tuple_t *t_point = MP_OBJ_TO_PTR2(p_obj, mp_obj_tuple_t); + points[p] = { + mp_obj_get_int(t_point->items[0]), + mp_obj_get_int(t_point->items[1]), + }; + } + + contours.push_back({points, t_contour->len}); + } + + self->graphics->polygon(contours); + + for(auto contour : contours) { + delete contour.points; + } + + return mp_const_none; +} + mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { size_t num_tuples = n_args - 1; const mp_obj_t *tuples = pos_args + 1; @@ -1027,7 +1082,7 @@ mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map // Check if there is only one argument, which might be a list if(n_args == 2) { - if(mp_obj_is_type(pos_args[1], &mp_type_list)) { + if(mp_obj_is_exact_type(pos_args[1], &mp_type_list)) { mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t); if(points->len <= 0) mp_raise_ValueError("poly(): cannot provide an empty list"); @@ -1044,7 +1099,7 @@ mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map std::vector points; for(size_t i = 0; i < num_tuples; i++) { mp_obj_t obj = tuples[i]; - if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple"); + if(!mp_obj_is_exact_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple"); mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t); diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 2503cbbf..167b6e02 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -93,6 +93,7 @@ extern mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_arg extern mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t ModPicoGraphics_pretty_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t ModPicoGraphics_triangle(size_t n_args, const mp_obj_t *args); extern mp_obj_t ModPicoGraphics_line(size_t n_args, const mp_obj_t *args);