PicoGraphics: Experimental Pretty Poly bring-up.

This commit is contained in:
Phil Howard 2022-09-16 15:19:20 +01:00 committed by Phil Howard
parent 57042bfed3
commit cc7219b44a
7 changed files with 498 additions and 7 deletions

View File

@ -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<pretty_poly::contour_t<int>> 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<int>(contours);
}
void PicoGraphics::polygon(const std::vector<Point> &points) {
pretty_poly::contour_t<int> contour(new pretty_poly::point_t<int>[points.size()], points.size());
for(auto i = 0; i < points.size(); i++) {
contour.points[i] = pretty_poly::point_t<int>(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<pretty_poly::contour_t<int>> 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) {

View File

@ -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<Point> &points);
void polygon(std::vector<pretty_poly::contour_t<int>> contours);
void triangle(Point p1, Point p2, Point p3);
void line(Point p1, Point p2);
void thick_line(Point p1, Point p2, uint thickness);

View File

@ -0,0 +1,129 @@
#pragma once
#include <cstdint>
#include <math.h>
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<typename T = int>
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<typename T> inline point_t<T> operator- (point_t<T> lhs, const point_t<T> &rhs) { lhs -= rhs; return lhs; }
template<typename T> inline point_t<T> operator- (const point_t<T> &rhs) { return point_t<T>(-rhs.x, -rhs.y); }
template<typename T> inline point_t<T> operator+ (point_t<T> lhs, const point_t<T> &rhs) { lhs += rhs; return lhs; }
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const float rhs) { lhs *= rhs; return lhs; }
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const point_t<T> &rhs) { lhs *= rhs; return lhs; }
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const mat3_t &rhs) { lhs *= rhs; return lhs; }
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const float rhs) { lhs /= rhs; return lhs; }
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const point_t<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<typename T>
struct contour_t {
point_t<T> *points;
unsigned count;
contour_t() {}
contour_t(vector<point_t<T>> v) : points(v.data()), count(v.size()) {};
contour_t(point_t<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);
}
};
}

View File

@ -0,0 +1,250 @@
#include <cstdint>
#include <algorithm>
#include <optional>
#include <cstring>
#include <new>
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<void(const tile_t &tile)> 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<int> &start, const point_t<int> &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<typename T>
void build_nodes(const contour_t<T> &contour, const tile_t &tile, point_t<int> origin = point_t<int>(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<int> 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<int> 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<typename T>
void draw_polygon(T *points, unsigned count) {
std::vector<contour_t<T>> contours;
contour_t<T> c(points, count);
contours.push_back(c);
draw_polygon<T>(contours);
}
template<typename T>
void draw_polygon(std::vector<contour_t<T>> contours, point_t<int> origin = point_t<int>(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<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);
}
}
}
}

View File

@ -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) },

View File

@ -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<pretty_poly::contour_t<int>> 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<int> *points = new pretty_poly::point_t<int>[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<Point> 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);

View File

@ -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);