From 7c5ebfce8c180a244bd4007ad7c41af74de9852c Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Wed, 16 Aug 2023 13:10:53 +0100 Subject: [PATCH] PicoVector: Matrix transforms and polygon type. --- libraries/pico_vector/pico_vector.cpp | 27 +- libraries/pico_vector/pico_vector.hpp | 13 +- libraries/pico_vector/pretty_poly.cpp | 9 +- libraries/pico_vector/pretty_poly_types.hpp | 7 +- micropython/modules/picovector/picovector.c | 76 ++++- micropython/modules/picovector/picovector.cpp | 278 ++++++++++++------ micropython/modules/picovector/picovector.h | 18 +- 7 files changed, 312 insertions(+), 116 deletions(-) diff --git a/libraries/pico_vector/pico_vector.cpp b/libraries/pico_vector/pico_vector.cpp index 3c0314e6..1cc318db 100644 --- a/libraries/pico_vector/pico_vector.cpp +++ b/libraries/pico_vector/pico_vector.cpp @@ -2,14 +2,14 @@ #include namespace pimoroni { - void PicoVector::polygon(std::vector> contours, Point origin, int scale) { - pretty_poly::draw_polygon( + void PicoVector::polygon(std::vector> contours, Point origin, int scale) { + pretty_poly::draw_polygon( contours, pretty_poly::point_t(origin.x, origin.y), scale); } - void PicoVector::rotate(std::vector> &contours, Point origin, float angle) { + void PicoVector::rotate(std::vector> &contours, Point origin, float angle) { pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y); pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y); angle = 2 * M_PI * (angle / 360.0f); @@ -23,7 +23,7 @@ namespace pimoroni { } } - void PicoVector::translate(std::vector> &contours, Point translation) { + void PicoVector::translate(std::vector> &contours, Point translation) { pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y); for(auto &contour : contours) { for(auto i = 0u; i < contour.count; i++) { @@ -32,6 +32,25 @@ namespace pimoroni { } } + void PicoVector::rotate(pretty_poly::contour_t &contour, Point origin, float angle) { + pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y); + pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y); + angle = 2 * M_PI * (angle / 360.0f); + pretty_poly::mat3_t r = pretty_poly::mat3_t::rotation(angle); + for(auto i = 0u; i < contour.count; i++) { + contour.points[i] *= t1; + contour.points[i] *= r; + contour.points[i] *= t2; + } + } + + void PicoVector::translate(pretty_poly::contour_t &contour, Point translation) { + pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y); + for(auto i = 0u; i < contour.count; i++) { + contour.points[i] *= t; + } + } + Point PicoVector::text(std::string_view text, Point origin) { // TODO: Normalize types somehow, so we're not converting? pretty_poly::point_t caret = pretty_poly::point_t(origin.x, origin.y); diff --git a/libraries/pico_vector/pico_vector.hpp b/libraries/pico_vector/pico_vector.hpp index 75c6cbdb..11ccebd3 100644 --- a/libraries/pico_vector/pico_vector.hpp +++ b/libraries/pico_vector/pico_vector.hpp @@ -3,6 +3,10 @@ #include "pico_graphics.hpp" namespace pimoroni { + + // Integer point types cause compound error in transformations + typedef float picovector_point_type; + class PicoVector { private: PicoGraphics *graphics; @@ -59,12 +63,15 @@ namespace pimoroni { return result; } - void rotate(std::vector> &contours, Point origin, float angle); - void translate(std::vector> &contours, Point translation); + void rotate(std::vector> &contours, Point origin, float angle); + void translate(std::vector> &contours, Point translation); + + void rotate(pretty_poly::contour_t &contour, Point origin, float angle); + void translate(pretty_poly::contour_t &contour, Point translation); Point text(std::string_view text, Point origin); - void polygon(std::vector> contours, Point origin = Point(0, 0), int scale=65536); + void polygon(std::vector> contours, Point origin = Point(0, 0), int scale=65536); static constexpr size_t pretty_poly_buffer_size() { return pretty_poly::buffer_size(); diff --git a/libraries/pico_vector/pretty_poly.cpp b/libraries/pico_vector/pretty_poly.cpp index 3ff44c33..78617ca6 100644 --- a/libraries/pico_vector/pretty_poly.cpp +++ b/libraries/pico_vector/pretty_poly.cpp @@ -122,14 +122,14 @@ namespace pretty_poly { // 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 + (((int(contour.points[contour.count - 1].x) * scale) << settings::antialias) / 65536) + ox, + (((int(contour.points[contour.count - 1].y) * scale) << settings::antialias) / 65536) + oy ); for(auto i = 0u; 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 + (((int(contour.points[i].x) * scale) << settings::antialias) / 65536) + ox, + (((int(contour.points[i].y) * scale) << settings::antialias) / 65536) + oy ); add_line_segment_to_nodes(last, point); @@ -234,5 +234,6 @@ namespace pretty_poly { } template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); +template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); \ No newline at end of file diff --git a/libraries/pico_vector/pretty_poly_types.hpp b/libraries/pico_vector/pretty_poly_types.hpp index 896acc52..51076f8a 100644 --- a/libraries/pico_vector/pretty_poly_types.hpp +++ b/libraries/pico_vector/pretty_poly_types.hpp @@ -36,7 +36,7 @@ namespace pretty_poly { 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;} + 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) { @@ -56,8 +56,9 @@ namespace pretty_poly { 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); + float tx = x, ty = y; + this->x = (m.v00 * tx + m.v01 * ty + m.v02); + this->y = (m.v10 * tx + m.v11 * ty + m.v12); } }; diff --git a/micropython/modules/picovector/picovector.c b/micropython/modules/picovector/picovector.c index 8b9b6be8..53579737 100644 --- a/micropython/modules/picovector/picovector.c +++ b/micropython/modules/picovector/picovector.c @@ -1,21 +1,81 @@ #include "picovector.h" -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_polygon_obj, 2, VECTOR_polygon); -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_regular_polygon_obj, 6, VECTOR_regular_polygon); +/* Polygon */ + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(POLYGON__del__obj, POLYGON__del__); + + +STATIC const mp_rom_map_elem_t POLYGON_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&POLYGON__del__obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(POLYGON_locals_dict, POLYGON_locals_dict_table); + +#ifdef MP_DEFINE_CONST_OBJ_TYPE +MP_DEFINE_CONST_OBJ_TYPE( + POLYGON_type, + MP_QSTR_polygon, + MP_TYPE_FLAG_NONE, + make_new, POLYGON_make_new, + print, POLYGON_print, + locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict +); +MP_DEFINE_CONST_OBJ_TYPE( + REGULAR_POLYGON_type, + MP_QSTR_regular_polygon, + MP_TYPE_FLAG_NONE, + make_new, REGULAR_POLYGON_make_new, + locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict +); +MP_DEFINE_CONST_OBJ_TYPE( + RECTANGLE_type, + MP_QSTR_rectangle, + MP_TYPE_FLAG_NONE, + make_new, RECTANGLE_make_new, + locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict +); +#else +const mp_obj_type_t POLYGON_type = { + { &mp_type_type }, + .name = MP_QSTR_polygon, + .make_new = POLYGON_make_new, + .print = POLYGON_print, + .locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict, +}; +const mp_obj_type_t REGULAR_POLYGON_type = { + { &mp_type_type }, + .name = MP_QSTR_regular_polygon, + .make_new = REGULAR_POLYGON_make_new, + .locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict, +}; +const mp_obj_type_t RECTANGLE_type = { + { &mp_type_type }, + .name = MP_QSTR_rectangle, + .make_new = RECTANGLE_make_new, + .locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict, +}; +#endif + +/* PicoVector */ + STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_text_obj, 4, VECTOR_text); STATIC MP_DEFINE_CONST_FUN_OBJ_3(VECTOR_set_font_obj, VECTOR_set_font); STATIC MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_size_obj, VECTOR_set_font_size); STATIC MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_antialiasing_obj, VECTOR_set_antialiasing); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_draw_obj, 2, VECTOR_draw); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_rotate_obj, 3, VECTOR_rotate); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_translate_obj, 4, VECTOR_translate); -// class STATIC const mp_rom_map_elem_t VECTOR_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&VECTOR_polygon_obj) }, - { MP_ROM_QSTR(MP_QSTR_regular_polygon), MP_ROM_PTR(&VECTOR_regular_polygon_obj) }, { MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&VECTOR_set_font_obj) }, { MP_ROM_QSTR(MP_QSTR_set_font_size), MP_ROM_PTR(&VECTOR_set_font_size_obj) }, { MP_ROM_QSTR(MP_QSTR_set_antialiasing), MP_ROM_PTR(&VECTOR_set_antialiasing_obj) }, { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) }, + + { MP_ROM_QSTR(MP_QSTR_draw), MP_ROM_PTR(&VECTOR_draw_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotate), MP_ROM_PTR(&VECTOR_rotate_obj) }, + { MP_ROM_QSTR(MP_QSTR_translate), MP_ROM_PTR(&VECTOR_translate_obj) }, }; STATIC MP_DEFINE_CONST_DICT(VECTOR_locals_dict, VECTOR_locals_dict_table); @@ -37,10 +97,14 @@ const mp_obj_type_t VECTOR_type = { }; #endif -// module +/* Module */ + STATIC const mp_map_elem_t VECTOR_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_picovector) }, { MP_OBJ_NEW_QSTR(MP_QSTR_PicoVector), (mp_obj_t)&VECTOR_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Polygon), (mp_obj_t)&POLYGON_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RegularPolygon), (mp_obj_t)®ULAR_POLYGON_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_Rectangle), (mp_obj_t)&RECTANGLE_type }, { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_NONE), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X4), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X16), MP_ROM_INT(2) }, diff --git a/micropython/modules/picovector/picovector.cpp b/micropython/modules/picovector/picovector.cpp index 848d239c..b5db5beb 100644 --- a/micropython/modules/picovector/picovector.cpp +++ b/micropython/modules/picovector/picovector.cpp @@ -23,6 +23,10 @@ typedef struct _VECTOR_obj_t { PicoVector *vector; } _VECTOR_obj_t; +typedef struct _POLYGON_obj_t { + mp_obj_base_t base; + pretty_poly::contour_t contour; +} _POLYGON_obj_t; pretty_poly::file_io::file_io(std::string_view filename) { mp_obj_t fn = mp_obj_new_str(filename.data(), (mp_uint_t)filename.size()); @@ -103,6 +107,139 @@ static const std::string_view mp_obj_to_string_r(const mp_obj_t &obj) { mp_raise_TypeError("can't convert object to str implicitly"); } +/* POLYGON */ + +mp_obj_t RECTANGLE_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_x, ARG_y, ARG_w, ARG_h }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t); + self->base.type = &POLYGON_type; + + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + int w = args[ARG_w].u_int; + int h = args[ARG_h].u_int; + + self->contour.points = m_new(pretty_poly::point_t, 4); + self->contour.count = 4; + + self->contour.points[0] = {float(x), float(y)}; + self->contour.points[1] = {float(x + w), float(y)}; + self->contour.points[2] = {float(x + w), float(y + h)}; + self->contour.points[3] = {float(x), float(y + h)}; + + return self; +} + +mp_obj_t REGULAR_POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_x, ARG_y, ARG_sides, ARG_radius, ARG_rotation }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_sides, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rotation, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t); + self->base.type = &POLYGON_type; + + Point origin(args[ARG_x].u_int, args[ARG_y].u_int); + unsigned int sides = args[ARG_sides].u_int; + float radius = mp_obj_get_float(args[ARG_radius].u_obj); + float rotation = 0.0f; + if (args[ARG_rotation].u_obj != mp_const_none) { + rotation = mp_obj_get_float(args[ARG_rotation].u_obj); + rotation *= (M_PI / 180.0f); + } + int o_x = args[ARG_x].u_int; + int o_y = args[ARG_y].u_int; + + float angle = (360.0f / sides) * (M_PI / 180.0f); + + self->contour.points = m_new(pretty_poly::point_t, sides); + self->contour.count = sides; + + for(auto s = 0u; s < sides; s++) { + float current_angle = angle * s + rotation; + self->contour.points[s] = { + (picovector_point_type)(cos(current_angle) * radius) + o_x, + (picovector_point_type)(sin(current_angle) * radius) + o_y + }; + } + + return self; +} + +mp_obj_t POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + _POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t); + self->base.type = &POLYGON_type; + + size_t num_points = n_args; + const mp_obj_t *points = all_args; + + if(num_points < 3) mp_raise_ValueError("Polygon: At least 3 points required."); + + self->contour.points = m_new(pretty_poly::point_t, num_points); + self->contour.count = num_points; + + for(auto i = 0u; i < num_points; i++) { + mp_obj_t c_obj = points[i]; + + if(!mp_obj_is_exact_type(c_obj, &mp_type_tuple)) mp_raise_ValueError("Not a tuple"); + + mp_obj_tuple_t *t_point = MP_OBJ_TO_PTR2(c_obj, mp_obj_tuple_t); + + if(t_point->len != 2) mp_raise_ValueError("Tuple must have X, Y"); + + self->contour.points[i] = { + (picovector_point_type)mp_obj_get_int(t_point->items[0]), + (picovector_point_type)mp_obj_get_int(t_point->items[1]), + }; + } + + return self; +} + +void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + _POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t); + + mp_print_str(print, "Polygon("); + mp_print_str(print, ", points = "); + mp_obj_print_helper(print, mp_obj_new_int(self->contour.count), PRINT_REPR); + mp_print_str(print, ", bounds = "); + mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().x), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().y), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().w), PRINT_REPR); + mp_print_str(print, ", "); + mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().h), PRINT_REPR); + mp_print_str(print, ")"); +} + +mp_obj_t POLYGON__del__(mp_obj_t self_in) { + _POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t); + (void)self; + // TODO: Do we actually need to free anything here, if it's on GC heap it should get collected + return mp_const_none; +} + +/* VECTOR */ + mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_picographics @@ -186,15 +323,14 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) return mp_const_none; } -mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_x, ARG_y, ARG_sides, ARG_radius, ARG_rotation }; +mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_polygon, ARG_angle, ARG_origin_x, ARG_origin_y }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_sides, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_rotation, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_polygon, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_angle, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_origin_x, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_origin_y, MP_ARG_INT, {.u_int = 0} } }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -202,107 +338,63 @@ mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_ _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _VECTOR_obj_t); - Point origin(args[ARG_x].u_int, args[ARG_y].u_int); - unsigned int sides = args[ARG_sides].u_int; - float radius = mp_obj_get_float(args[ARG_radius].u_obj); - float rotation = mp_obj_get_float(args[ARG_rotation].u_obj); - int o_x = args[ARG_x].u_int; - int o_y = args[ARG_y].u_int; + if(!MP_OBJ_IS_TYPE(args[ARG_polygon].u_obj, &POLYGON_type)) mp_raise_TypeError("rotate: polygon required"); - float angle = (360.0f / sides) * (M_PI / 180.0f); + _POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(args[ARG_polygon].u_obj, _POLYGON_obj_t); - pretty_poly::point_t *points = new pretty_poly::point_t[sides]; + Point origin = Point(args[ARG_origin_x].u_int, args[ARG_origin_y].u_int); - for(auto s = 0u; s < sides; s++) { - float current_angle = angle * s + rotation; - points[s] = { - int(cos(current_angle) * radius), - int(sin(current_angle) * radius) - }; - } + float angle = mp_obj_get_float(args[ARG_angle].u_obj); - std::vector> contours; - contours.push_back({points, sides}); - self->vector->polygon(contours, Point(o_x, o_y)); - - delete points; + self->vector->rotate(poly->contour, origin, angle); return mp_const_none; } -mp_obj_t VECTOR_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { +mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_polygon, ARG_x, ARG_y }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_polygon, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT, {.u_int = 0} } + }; - size_t num_tuples = n_args - 1; - const mp_obj_t *lists = pos_args + 1; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _VECTOR_obj_t); + + if(!MP_OBJ_IS_TYPE(args[ARG_polygon].u_obj, &POLYGON_type)) mp_raise_TypeError("rotate: polygon required"); + + _POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(args[ARG_polygon].u_obj, _POLYGON_obj_t); + + Point translate = Point(args[ARG_x].u_int, args[ARG_y].u_int); + + self->vector->translate(poly->contour, translate); + + return mp_const_none; +} + +mp_obj_t VECTOR_draw(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + size_t num_polygons = n_args - 1; + const mp_obj_t *polygons = pos_args + 1; _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], _VECTOR_obj_t); - int offset_x = 0; - int offset_y = 0; - float angle = 0.0f; + std::vector> contours; - // TODO: We should probably convert this to a full-fat argument parser - // with optional kwargs for translation and rotation - // this is very, very hacky - if(mp_obj_is_int(pos_args[1])) { - offset_x = mp_obj_get_int(pos_args[1]); - lists++; - num_tuples--; + for(auto i = 0u; i < num_polygons; i++) { + mp_obj_t poly_obj = polygons[i]; + + if(!MP_OBJ_IS_TYPE(poly_obj, &POLYGON_type)) mp_raise_TypeError("draw: Polygon required."); + + _POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(poly_obj, _POLYGON_obj_t); + contours.push_back(poly->contour); } - if (mp_obj_is_int(pos_args[2])) { - offset_y = mp_obj_get_int(pos_args[2]); - lists++; - num_tuples--; - } - - if (mp_obj_is_float(pos_args[3])) { - angle = mp_obj_get_float(pos_args[3]); - lists++; - num_tuples--; - } - - std::vector> contours; - - for(auto i = 0u; 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 = 0u; 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}); - } - - // TODO: This is pretty awful - // Translating contours could be another operation - // But it's costly to convert to/from a list of lists of tuples - // Perhaps polygons should be a purely internal C++ concept - // And we could have make_polygon(list(tuple, tuple)) ? - if(angle != 0.0f) { - self->vector->rotate(contours, Point(offset_x, offset_y), angle); - self->vector->polygon(contours, Point(0, 0)); - } else { - self->vector->polygon(contours, Point(offset_x, offset_y)); - } - - for(auto contour : contours) { - delete contour.points; - } + self->vector->polygon(contours); return mp_const_none; } diff --git a/micropython/modules/picovector/picovector.h b/micropython/modules/picovector/picovector.h index 16a59fce..8b99557b 100644 --- a/micropython/modules/picovector/picovector.h +++ b/micropython/modules/picovector/picovector.h @@ -2,12 +2,24 @@ #include "py/objstr.h" extern const mp_obj_type_t VECTOR_type; +extern const mp_obj_type_t POLYGON_type; +extern const mp_obj_type_t REGULAR_POLYGON_type; +extern const mp_obj_type_t RECTANGLE_type; + +extern mp_obj_t POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t REGULAR_POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t RECTANGLE_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); + +extern mp_obj_t POLYGON__del__(mp_obj_t self_in); extern mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); -extern mp_obj_t VECTOR_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t VECTOR_set_font(mp_obj_t self_in, mp_obj_t font, mp_obj_t size); extern mp_obj_t VECTOR_set_font_size(mp_obj_t self_in, mp_obj_t size); -extern mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa); \ No newline at end of file +extern mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa); + +extern mp_obj_t VECTOR_draw(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file