From 206d0e598adb66e2b23a39a7d11e2f5087513205 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Sat, 2 May 2020 02:21:35 -0700 Subject: Add vectorio: for drawing shapes vectorio builds on m4 express feather Concrete shapes are composed into a VectorShape which is put into a displayio Group for display. VectorShape provides transpose and x/y positioning for shape implementations. Included Shapes: * Circle - A radius; Circle is positioned at its axis in the VectorShape. - You can freely modify the radius to grow and shrink the circle in-place. * Polygon - An ordered list of points. - Beteween each successive point an edge is inferred. A final edge closing the shape is inferred between the last point and the first point. - You can modify the points in a Polygon. The points' coordinate system is relative to (0, 0) so if you'd like a top-center justified 10x20 rectangle you can do points [(-5, 0), (5, 0), (5, 20), (0, 20)] and your VectorShape x and y properties will position the rectangle relative to its top center point * Rectangle A width and a height. --- shared-bindings/vectorio/Circle.c | 79 ++++++++++++ shared-bindings/vectorio/Circle.h | 22 ++++ shared-bindings/vectorio/Polygon.c | 139 +++++++++++++++++++++ shared-bindings/vectorio/Polygon.h | 24 ++++ shared-bindings/vectorio/Rectangle.c | 56 +++++++++ shared-bindings/vectorio/Rectangle.h | 15 +++ shared-bindings/vectorio/VectorShape.c | 213 +++++++++++++++++++++++++++++++++ shared-bindings/vectorio/VectorShape.h | 25 ++++ shared-bindings/vectorio/__init__.c | 46 +++++++ 9 files changed, 619 insertions(+) create mode 100644 shared-bindings/vectorio/Circle.c create mode 100644 shared-bindings/vectorio/Circle.h create mode 100644 shared-bindings/vectorio/Polygon.c create mode 100644 shared-bindings/vectorio/Polygon.h create mode 100644 shared-bindings/vectorio/Rectangle.c create mode 100644 shared-bindings/vectorio/Rectangle.h create mode 100644 shared-bindings/vectorio/VectorShape.c create mode 100644 shared-bindings/vectorio/VectorShape.h create mode 100644 shared-bindings/vectorio/__init__.c (limited to 'shared-bindings') diff --git a/shared-bindings/vectorio/Circle.c b/shared-bindings/vectorio/Circle.c new file mode 100644 index 000000000..8f409bd65 --- /dev/null +++ b/shared-bindings/vectorio/Circle.c @@ -0,0 +1,79 @@ + +#include "shared-bindings/vectorio/Circle.h" + + +#include + +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| .. currentmodule:: vectorio +//| +//| :class:`Circle` -- Represents a circle by its radius +//| ========================================================================== +//| +//| .. class:: Circle(radius) +//| +//| :param int radius: The radius of the circle in pixels +//| +static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_radius }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + 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); + + mp_int_t radius = args[ARG_radius].u_int; + if (radius < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_radius); + } + + vectorio_circle_t *self = m_new_obj(vectorio_circle_t); + self->base.type = &vectorio_circle_type; + common_hal_vectorio_circle_construct(self, radius); + + return MP_OBJ_FROM_PTR(self); +} + + +//| .. attribute:: radius +//| +//| Update the radius of the circle +//| +STATIC mp_obj_t vectorio_circle_obj_get_radius(mp_obj_t self_in) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_vectorio_circle_get_radius(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_circle_get_radius_obj, vectorio_circle_obj_get_radius); + +STATIC mp_obj_t vectorio_circle_obj_set_radius(mp_obj_t self_in, mp_obj_t radius) { + vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_vectorio_circle_set_radius(self, mp_obj_get_int(radius)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_circle_set_radius_obj, vectorio_circle_obj_set_radius); + +const mp_obj_property_t vectorio_circle_radius_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_circle_get_radius_obj, + (mp_obj_t)&vectorio_circle_set_radius_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_circle_locals_dict, vectorio_circle_locals_dict_table); + +const mp_obj_type_t vectorio_circle_type = { + { &mp_type_type }, + .name = MP_QSTR_Circle, + .make_new = vectorio_circle_make_new, + .locals_dict = (mp_obj_dict_t*)&vectorio_circle_locals_dict, +}; + diff --git a/shared-bindings/vectorio/Circle.h b/shared-bindings/vectorio/Circle.h new file mode 100644 index 000000000..e8fc048eb --- /dev/null +++ b/shared-bindings/vectorio/Circle.h @@ -0,0 +1,22 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H + +#include "shared-module/vectorio/__init__.h" +#include "shared-module/vectorio/Circle.h" +#include "shared-module/displayio/area.h" + +extern const mp_obj_type_t vectorio_circle_type; + +void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius); + +void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t notification); + +uint32_t common_hal_vectorio_circle_get_pixel(void *circle, int16_t x, int16_t y); + +void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area); + + +int16_t common_hal_vectorio_circle_get_radius(void *circle); +void common_hal_vectorio_circle_set_radius(void *circle, int16_t radius); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H diff --git a/shared-bindings/vectorio/Polygon.c b/shared-bindings/vectorio/Polygon.c new file mode 100644 index 000000000..b8bb377ac --- /dev/null +++ b/shared-bindings/vectorio/Polygon.c @@ -0,0 +1,139 @@ + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/Polygon.h" + +#include + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +#define VECTORIO_POLYGON_DEBUG(...) (void)0 +// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +// Converts a list of points tuples to a flat list of ints for speedier internal use. +// Also validates the points. +static mp_obj_t _to_points_list(mp_obj_t points_tuple_list) { + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(points_tuple_list, &len, &items); + VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len); + + if ( len == 0 ) { + mp_raise_TypeError_varg(translate("empty %q list"), MP_QSTR_point); + } + + mp_obj_t points_list = mp_obj_new_list(0, NULL); + + for ( size_t i = 0; i < len; ++i) { + size_t tuple_len = 0; + mp_obj_t *tuple_items; + mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); + + if (tuple_len != 2) { + mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); + } + int value; + if (!mp_obj_get_int_maybe(tuple_items[0], &value)) { + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); + if (!mp_obj_get_int_maybe(tuple_items[1], &value)) { + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value)); + } + return points_list; +} + + + +//| .. currentmodule:: vectorio +//| +//| :class:`Polygon` -- Represents a closed shape by ordered vertices +//| ========================================================================== +//| +//| .. class:: Polygon( List[ Tuple[ x, y ], ... ] ) +//| +//| :param [Point] points_array: Vertices for the polygon +//| +static mp_obj_t vectorio_polygon_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_points_list }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + 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); + + if (!MP_OBJ_IS_TYPE(args[ARG_points_list].u_obj, &mp_type_list)) { + mp_raise_TypeError_varg(translate("%q list must be a list"), MP_QSTR_point); + } + mp_obj_t points_list = _to_points_list(args[ARG_points_list].u_obj); + + vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t); + self->base.type = &vectorio_polygon_type; + + common_hal_vectorio_polygon_construct(self, points_list); + + return MP_OBJ_FROM_PTR(self); +} + + +//| .. attribute:: points +//| +//| Set a new look and shape for this polygon +//| +STATIC mp_obj_t vectorio_polygon_obj_get_points(mp_obj_t self_in) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t list = mp_obj_new_list(0, NULL); + + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(common_hal_vectorio_polygon_get_points(self), &len, &items); + + for (size_t i = 0; i < len; i += 2) { + mp_obj_t tuple[] = { items[i], items[i+1] }; + mp_obj_list_append( + list, + mp_obj_new_tuple(2, tuple) + ); + } + return list; +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_get_points); + +STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) { + vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in); + + mp_obj_t points_list = _to_points_list(points); + + common_hal_vectorio_polygon_set_points(self, points_list); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points); + +const mp_obj_property_t vectorio_polygon_points_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_polygon_get_points_obj, + (mp_obj_t)&vectorio_polygon_set_points_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + + +STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_polygon_locals_dict, vectorio_polygon_locals_dict_table); + +const mp_obj_type_t vectorio_polygon_type = { + { &mp_type_type }, + .name = MP_QSTR_Polygon, + .make_new = vectorio_polygon_make_new, + .locals_dict = (mp_obj_dict_t*)&vectorio_polygon_locals_dict, +}; + diff --git a/shared-bindings/vectorio/Polygon.h b/shared-bindings/vectorio/Polygon.h new file mode 100644 index 000000000..5594fbec4 --- /dev/null +++ b/shared-bindings/vectorio/Polygon.h @@ -0,0 +1,24 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H + +#include "shared-module/vectorio/Polygon.h" +#include "shared-module/displayio/area.h" +#include "shared-module/vectorio/__init__.h" + +extern const mp_obj_type_t vectorio_polygon_type; + +void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list); +void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification); + + +uint32_t common_hal_vectorio_polygon_get_pixel(void *polygon, int16_t x, int16_t y); + +void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *out_area); + + + +mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self); +void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list); + + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H diff --git a/shared-bindings/vectorio/Rectangle.c b/shared-bindings/vectorio/Rectangle.c new file mode 100644 index 000000000..ea468f788 --- /dev/null +++ b/shared-bindings/vectorio/Rectangle.c @@ -0,0 +1,56 @@ + +#include "shared-bindings/vectorio/Rectangle.h" + +#include + +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| .. currentmodule:: vectorio +//| +//| :class:`Rectangle` -- Represents a rectangle by defining its bounds +//| ========================================================================== +//| +//| .. class:: Rectangle(width, height) +//| +//| :param int width: The number of pixels wide +//| :param int height: The number of pixels high +//| +static mp_obj_t vectorio_rectangle_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_width, ARG_height }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT }, + }; + 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); + + mp_int_t width = args[ARG_width].u_int; + if (width < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_width); + } + mp_int_t height = args[ARG_height].u_int; + if (height < 1) { + mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_height); + } + + vectorio_rectangle_t *self = m_new_obj(vectorio_rectangle_t); + self->base.type = &vectorio_rectangle_type; + common_hal_vectorio_rectangle_construct(self, width, height); + + return MP_OBJ_FROM_PTR(self); +} + + +STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = { +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_rectangle_locals_dict, vectorio_rectangle_locals_dict_table); + +const mp_obj_type_t vectorio_rectangle_type = { + { &mp_type_type }, + .name = MP_QSTR_Rectangle, + .make_new = vectorio_rectangle_make_new, + .locals_dict = (mp_obj_dict_t*)&vectorio_rectangle_locals_dict, +}; diff --git a/shared-bindings/vectorio/Rectangle.h b/shared-bindings/vectorio/Rectangle.h new file mode 100644 index 000000000..bb461ed9d --- /dev/null +++ b/shared-bindings/vectorio/Rectangle.h @@ -0,0 +1,15 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H + +#include "shared-module/vectorio/Rectangle.h" +#include "shared-module/displayio/area.h" + +extern const mp_obj_type_t vectorio_rectangle_type; + +void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height); + +uint32_t common_hal_vectorio_rectangle_get_pixel(void *rectangle, int16_t x, int16_t y); + +void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c new file mode 100644 index 000000000..8f99577b4 --- /dev/null +++ b/shared-bindings/vectorio/VectorShape.c @@ -0,0 +1,213 @@ + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/VectorShape.h" +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" + +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Palette.h" + +#include + +#include "lib/utils/context_manager_helpers.h" + +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "supervisor/shared/translate.h" + + +//| .. currentmodule:: vectorio +//| +//| :class:`VectorShape` -- Binds a vector shape to a location and pixel color +//| ========================================================================== +//| +//| .. class:: VectorShape( shape, pixel_shader, x=0, y=0) +//| +//| :param vectorio.Polygon shape: The shape to draw. +//| :param displayio.Palette pixel_shader: The pixel shader that produces colors from values +//| :param int x: Initial x position of the center axis of the shape within the parent. +//| :param int y: Initial y position of the center axis of the shape within the parent. +//| +STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y, ARG_transpose_xy }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_shape, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, + { MP_QSTR_transpose_xy, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + 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); + + mp_obj_t pixel_shader = args[ARG_pixel_shader].u_obj; + if (!MP_OBJ_IS_TYPE(pixel_shader, &displayio_colorconverter_type) && + !MP_OBJ_IS_TYPE(pixel_shader, &displayio_palette_type)) { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_pixel_shader); + } + + int16_t x = args[ARG_x].u_int; + int16_t y = args[ARG_y].u_int; + + mp_obj_t shape = args[ARG_shape].u_obj; + vectorio_ishape_t ishape; + // Wire up shape functions + if (MP_OBJ_IS_TYPE(shape, &vectorio_polygon_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_polygon_get_area; + ishape.get_pixel = &common_hal_vectorio_polygon_get_pixel; + } else if (MP_OBJ_IS_TYPE(shape, &vectorio_rectangle_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_rectangle_get_area; + ishape.get_pixel = &common_hal_vectorio_rectangle_get_pixel; + } else if (MP_OBJ_IS_TYPE(shape, &vectorio_circle_type)) { + ishape.shape = shape; + ishape.get_area = &common_hal_vectorio_circle_get_area; + ishape.get_pixel = &common_hal_vectorio_circle_get_pixel; + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_shape); + } + + vectorio_vector_shape_t *self = m_new_obj(vectorio_vector_shape_t); + self->base.type = &vectorio_vector_shape_type; + common_hal_vectorio_vector_shape_construct(self, + ishape, pixel_shader, x, y, args[ARG_transpose_xy].u_bool + ); + + // Wire up event callbacks + vectorio_event_t on_dirty = { + .obj = self, + .event = &common_hal_vectorio_vector_shape_set_dirty + }; + + if (MP_OBJ_IS_TYPE(shape, &vectorio_polygon_type)) { + common_hal_vectorio_polygon_set_on_dirty(self->ishape.shape, on_dirty); + } else if (MP_OBJ_IS_TYPE(shape, &vectorio_rectangle_type)) { + common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); + } else if (MP_OBJ_IS_TYPE(shape, &vectorio_circle_type)) { + } else { + mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_shape); + } + + return MP_OBJ_FROM_PTR(self); +} + + +//| .. attribute:: x +//| +//| X position of the center point of the shape in the parent. +//| +STATIC mp_obj_t vectorio_vector_shape_obj_get_x(mp_obj_t self_in) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_x(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_x_obj, vectorio_vector_shape_obj_get_x); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_x(mp_obj_t self_in, mp_obj_t x_obj) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t x = mp_obj_get_int(x_obj); + common_hal_vectorio_vector_shape_set_x(self, x); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_x_obj, vectorio_vector_shape_obj_set_x); + +const mp_obj_property_t vectorio_vector_shape_x_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_vector_shape_get_x_obj, + (mp_obj_t)&vectorio_vector_shape_set_x_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| .. attribute:: y +//| +//| Y position of the center point of the shape in the parent. +//| +STATIC mp_obj_t vectorio_vector_shape_obj_get_y(mp_obj_t self_in) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_vectorio_vector_shape_get_y(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_y_obj, vectorio_vector_shape_obj_get_y); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_y(mp_obj_t self_in, mp_obj_t y_obj) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + + mp_int_t y = mp_obj_get_int(y_obj); + common_hal_vectorio_vector_shape_set_y(self, y); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_y_obj, vectorio_vector_shape_obj_set_y); + +const mp_obj_property_t vectorio_vector_shape_y_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_vector_shape_get_y_obj, + (mp_obj_t)&vectorio_vector_shape_set_y_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +//| .. attribute:: transpose_xy +//| +//| true if the object is to be flipped. +//| +STATIC mp_obj_t vectorio_vector_shape_obj_get_transpose_xy(mp_obj_t self_in) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(common_hal_vectorio_vector_shape_get_transpose_xy(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_transpose_xy_obj, vectorio_vector_shape_obj_get_transpose_xy); + +const mp_obj_property_t vectorio_vector_shape_transpose_xy_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_vector_shape_get_transpose_xy_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. attribute:: pixel_shader +//| +//| The pixel shader of the shape. +//| +STATIC mp_obj_t vectorio_vector_shape_obj_get_pixel_shader(mp_obj_t self_in) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_vectorio_vector_shape_get_pixel_shader(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_pixel_shader_obj, vectorio_vector_shape_obj_get_pixel_shader); + +STATIC mp_obj_t vectorio_vector_shape_obj_set_pixel_shader(mp_obj_t self_in, mp_obj_t pixel_shader) { + vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); + if (!MP_OBJ_IS_TYPE(pixel_shader, &displayio_palette_type) && !MP_OBJ_IS_TYPE(pixel_shader, &displayio_colorconverter_type)) { + mp_raise_TypeError(translate("pixel_shader must be displayio.Palette or displayio.ColorConverter")); + } + + common_hal_vectorio_vector_shape_set_pixel_shader(self, pixel_shader); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(vectorio_vector_shape_set_pixel_shader_obj, vectorio_vector_shape_obj_set_pixel_shader); + +const mp_obj_property_t vectorio_vector_shape_pixel_shader_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&vectorio_vector_shape_get_pixel_shader_obj, + (mp_obj_t)&vectorio_vector_shape_set_pixel_shader_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + + +STATIC const mp_rom_map_elem_t vectorio_vector_shape_locals_dict_table[] = { + // Properties + { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, + { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) }, + { MP_ROM_QSTR(MP_QSTR_pixel_shader), MP_ROM_PTR(&vectorio_vector_shape_pixel_shader_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(vectorio_vector_shape_locals_dict, vectorio_vector_shape_locals_dict_table); + +const mp_obj_type_t vectorio_vector_shape_type = { + { &mp_type_type }, + .name = MP_QSTR_VectorShape, + .make_new = vectorio_vector_shape_make_new, + .locals_dict = (mp_obj_dict_t*)&vectorio_vector_shape_locals_dict, +}; diff --git a/shared-bindings/vectorio/VectorShape.h b/shared-bindings/vectorio/VectorShape.h new file mode 100644 index 000000000..ad745b30f --- /dev/null +++ b/shared-bindings/vectorio/VectorShape.h @@ -0,0 +1,25 @@ +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H + +#include "shared-module/vectorio/VectorShape.h" + +extern const mp_obj_type_t vectorio_vector_shape_type; + +void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, + vectorio_ishape_t ishape, + mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy); + +void common_hal_vectorio_vector_shape_set_dirty(void *self); + +mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x); + +mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y); + +bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self); + +mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self); +void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H diff --git a/shared-bindings/vectorio/__init__.c b/shared-bindings/vectorio/__init__.c new file mode 100644 index 000000000..b9e3828bd --- /dev/null +++ b/shared-bindings/vectorio/__init__.c @@ -0,0 +1,46 @@ +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" +#include "shared-bindings/vectorio/VectorShape.h" + +//| :mod:`vectorio` --- Lightweight 2d shapes for displays +//| ========================================================================= +//| +//| .. module:: vectorio +//| :synopsis: Adds vector graphics to displayio +//| :platform: SAMD21, SAMD51, nRF52 +//| +//| The `vectorio` module contains classes to construct shapes +//| by describing their points rather than providing them in bitmaps. +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| Circle +//| Polygon +//| Rectangle +//| VectorShape +//| + + +STATIC const mp_rom_map_elem_t vectorio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_vectorio) }, + { MP_ROM_QSTR(MP_QSTR_Circle), MP_ROM_PTR(&vectorio_circle_type) }, + { MP_ROM_QSTR(MP_QSTR_Polygon), MP_ROM_PTR(&vectorio_polygon_type) }, + { MP_ROM_QSTR(MP_QSTR_Rectangle), MP_ROM_PTR(&vectorio_rectangle_type) }, + { MP_ROM_QSTR(MP_QSTR_VectorShape), MP_ROM_PTR(&vectorio_vector_shape_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(vectorio_module_globals, vectorio_module_globals_table); + +const mp_obj_module_t vectorio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&vectorio_module_globals, +}; -- cgit v1.2.3 From 4086600b61f14e043f02c1b8771e7780b82f313c Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Fri, 8 May 2020 23:03:15 -0700 Subject: vectorio: switch per-shape transform to Display Rather than maintain a transform per-shape, we'll just use whatever settings are on the Display. Currently only transpose is done. --- shared-bindings/vectorio/VectorShape.c | 22 ++-------------------- shared-bindings/vectorio/VectorShape.h | 8 +++++--- shared-module/displayio/Group.c | 6 ++++++ shared-module/vectorio/VectorShape.c | 34 ++++++++++++++++++++++++---------- shared-module/vectorio/VectorShape.h | 2 +- 5 files changed, 38 insertions(+), 34 deletions(-) (limited to 'shared-bindings') diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index 8f99577b4..02a42536d 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -32,13 +32,12 @@ //| :param int y: Initial y position of the center axis of the shape within the parent. //| STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y, ARG_transpose_xy }; + enum { ARG_shape, ARG_pixel_shader, ARG_x, ARG_y }; static const mp_arg_t allowed_args[] = { { MP_QSTR_shape, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_pixel_shader, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_x, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, { MP_QSTR_y, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} }, - { MP_QSTR_transpose_xy, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, }; 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); @@ -74,7 +73,7 @@ STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t vectorio_vector_shape_t *self = m_new_obj(vectorio_vector_shape_t); self->base.type = &vectorio_vector_shape_type; common_hal_vectorio_vector_shape_construct(self, - ishape, pixel_shader, x, y, args[ARG_transpose_xy].u_bool + ishape, pixel_shader, x, y ); // Wire up event callbacks @@ -150,23 +149,6 @@ const mp_obj_property_t vectorio_vector_shape_y_obj = { }; -//| .. attribute:: transpose_xy -//| -//| true if the object is to be flipped. -//| -STATIC mp_obj_t vectorio_vector_shape_obj_get_transpose_xy(mp_obj_t self_in) { - vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(common_hal_vectorio_vector_shape_get_transpose_xy(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(vectorio_vector_shape_get_transpose_xy_obj, vectorio_vector_shape_obj_get_transpose_xy); - -const mp_obj_property_t vectorio_vector_shape_transpose_xy_obj = { - .base.type = &mp_type_property, - .proxy = {(mp_obj_t)&vectorio_vector_shape_get_transpose_xy_obj, - (mp_obj_t)&mp_const_none_obj, - (mp_obj_t)&mp_const_none_obj}, -}; - //| .. attribute:: pixel_shader //| //| The pixel shader of the shape. diff --git a/shared-bindings/vectorio/VectorShape.h b/shared-bindings/vectorio/VectorShape.h index ad745b30f..d098504e9 100644 --- a/shared-bindings/vectorio/VectorShape.h +++ b/shared-bindings/vectorio/VectorShape.h @@ -2,12 +2,13 @@ #define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H #include "shared-module/vectorio/VectorShape.h" +#include "shared-module/displayio/area.h" extern const mp_obj_type_t vectorio_vector_shape_type; void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, vectorio_ishape_t ishape, - mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy); + mp_obj_t pixel_shader, uint16_t x, uint16_t y); void common_hal_vectorio_vector_shape_set_dirty(void *self); @@ -17,9 +18,10 @@ void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_in mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y); -bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self); - mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self); void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader); + +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform); + #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 9d50dabec..0c31de532 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -122,6 +122,12 @@ static void _update_child_transforms(displayio_group_t* self) { } for (size_t i = 0; i < self->size; i++) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_update_transform(layer, &self->absolute_transform); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_update_transform(layer, &self->absolute_transform); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c index 985b77858..6030c1e3d 100644 --- a/shared-module/vectorio/VectorShape.c +++ b/shared-module/vectorio/VectorShape.c @@ -50,7 +50,7 @@ inline __attribute__((always_inline)) static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { VECTORIO_SHAPE_DEBUG("%p get_screen_area\n", self); self->ishape.get_area(self->ishape.shape, out_area); - if (self->transpose_xy) { + if (self->absolute_transform->transpose_xy) { _transpose_area(out_area); displayio_area_shift(out_area, self->y, self->x); } else { @@ -76,18 +76,32 @@ void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { } +static displayio_buffer_transform_t null_transform = { + .x = 0, + .y = 0, + .dx = 0, + .dy = 0, + .scale = 1, + .width = 0, + .height = 0, + .mirror_x = false, + .mirror_y = false, + .transpose_xy = false +}; + + void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, vectorio_ishape_t ishape, - mp_obj_t pixel_shader, uint16_t x, uint16_t y, bool transpose_xy) { + mp_obj_t pixel_shader, uint16_t x, uint16_t y) { VECTORIO_SHAPE_DEBUG("%p vector_shape_construct x:%3d, y:%3d\n", self, x, y); self->x = x; self->y = y; self->pixel_shader = pixel_shader; self->ishape = ishape; - self->transpose_xy = transpose_xy; self->dirty = true; _get_screen_area(self, &self->ephemeral_dirty_area); self->ephemeral_dirty_area.next = NULL; + self->absolute_transform = &null_transform; } @@ -123,12 +137,6 @@ void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_in } -bool common_hal_vectorio_vector_shape_get_transpose_xy(vectorio_vector_shape_t *self) { - VECTORIO_SHAPE_DEBUG("%p get_transpose_xy\n", self); - return self->transpose_xy; -} - - mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) { VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self); return self->pixel_shader; @@ -191,7 +199,7 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ // Get the target pixel based on the shape's coordinate space int16_t pixel_to_get_x; int16_t pixel_to_get_y; - if (self->transpose_xy) { + if (self->absolute_transform->transpose_xy) { pixel_to_get_x = input_pixel.y - self->x; pixel_to_get_y = input_pixel.x - self->y; } else { @@ -280,3 +288,9 @@ displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_ return tail; } +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) { + self->absolute_transform = group_transform; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h index ac60e5b98..8fc698dbc 100644 --- a/shared-module/vectorio/VectorShape.h +++ b/shared-module/vectorio/VectorShape.h @@ -30,7 +30,7 @@ typedef struct { mp_obj_t pixel_shader; int16_t x; int16_t y; - bool transpose_xy; + displayio_buffer_transform_t *absolute_transform; bool dirty; // True if we need to draw // Tracks current shape footprint and expands outward as the shape dirties and changes. // This is suboptimal if you move your shape far. Could add more state to only redraw -- cgit v1.2.3 From 040beb0577f4360f34953db16c285a9af1bea6b8 Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Mon, 11 May 2020 21:15:40 -0700 Subject: Clean up circle documentation --- shared-bindings/vectorio/Circle.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'shared-bindings') diff --git a/shared-bindings/vectorio/Circle.c b/shared-bindings/vectorio/Circle.c index 8f409bd65..260e80d0f 100644 --- a/shared-bindings/vectorio/Circle.c +++ b/shared-bindings/vectorio/Circle.c @@ -17,6 +17,8 @@ //| //| .. class:: Circle(radius) //| +//| Circle is positioned on screen by its center point. +//| //| :param int radius: The radius of the circle in pixels //| static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -42,7 +44,7 @@ static mp_obj_t vectorio_circle_make_new(const mp_obj_type_t *type, size_t n_arg //| .. attribute:: radius //| -//| Update the radius of the circle +//| The radius of the circle in pixels. //| STATIC mp_obj_t vectorio_circle_obj_get_radius(mp_obj_t self_in) { vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in); -- cgit v1.2.3 From 78444a1018f5a9dbac6e2fac1b7385970accedbc Mon Sep 17 00:00:00 2001 From: warriorofwire <3454741+WarriorOfWire@users.noreply.github.com> Date: Mon, 11 May 2020 21:21:05 -0700 Subject: fix VectorShape on_dirty callback registration being set for the wrong type --- shared-bindings/vectorio/VectorShape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shared-bindings') diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index 02a42536d..4b39ec996 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -85,8 +85,8 @@ STATIC mp_obj_t vectorio_vector_shape_make_new(const mp_obj_type_t *type, size_t if (MP_OBJ_IS_TYPE(shape, &vectorio_polygon_type)) { common_hal_vectorio_polygon_set_on_dirty(self->ishape.shape, on_dirty); } else if (MP_OBJ_IS_TYPE(shape, &vectorio_rectangle_type)) { - common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); } else if (MP_OBJ_IS_TYPE(shape, &vectorio_circle_type)) { + common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); } else { mp_raise_TypeError_varg(translate("unsupported %q type"), MP_QSTR_shape); } -- cgit v1.2.3