Skip to content

Commit 206d0e5

Browse files
committed
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.
1 parent 90bd931 commit 206d0e5

25 files changed

+1276
-1
lines changed

ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ QSPI_FLASH_FILESYSTEM = 1
1010
EXTERNAL_FLASH_DEVICE_COUNT = 1
1111
EXTERNAL_FLASH_DEVICES = GD25Q16C
1212
LONGINT_IMPL = MPZ
13+
14+
CIRCUITPY_VECTORIO = 1
15+

py/circuitpy_defns.mk

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ endif
145145
ifeq ($(CIRCUITPY_DISPLAYIO),1)
146146
SRC_PATTERNS += displayio/% terminalio/% fontio/%
147147
endif
148+
ifeq ($(CIRCUITPY_VECTORIO),1)
149+
SRC_PATTERNS += vectorio/%
150+
endif
148151
ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1)
149152
SRC_PATTERNS += framebufferio/%
150153
endif
@@ -358,6 +361,11 @@ SRC_SHARED_MODULE_ALL = \
358361
displayio/Shape.c \
359362
displayio/TileGrid.c \
360363
displayio/__init__.c \
364+
vectorio/Circle.c \
365+
vectorio/Rectangle.c \
366+
vectorio/Polygon.c \
367+
vectorio/VectorShape.c \
368+
vectorio/__init__.c \
361369
fontio/BuiltinFont.c \
362370
fontio/__init__.c \
363371
framebufferio/FramebufferDisplay.c \

py/circuitpy_mpconfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ extern const struct _mp_obj_module_t framebufferio_module;
352352
#define FRAMEBUFFERIO_MODULE
353353
#endif
354354

355+
#if CIRCUITPY_VECTORIO
356+
extern const struct _mp_obj_module_t vectorio_module;
357+
#define VECTORIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_vectorio), (mp_obj_t)&vectorio_module },
358+
#endif
359+
355360
#if CIRCUITPY_FREQUENCYIO
356361
extern const struct _mp_obj_module_t frequencyio_module;
357362
#define FREQUENCYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_frequencyio), (mp_obj_t)&frequencyio_module },
@@ -642,6 +647,7 @@ extern const struct _mp_obj_module_t ustack_module;
642647
DISPLAYIO_MODULE \
643648
FONTIO_MODULE \
644649
TERMINALIO_MODULE \
650+
VECTORIO_MODULE \
645651
ERRNO_MODULE \
646652
FRAMEBUFFERIO_MODULE \
647653
FREQUENCYIO_MODULE \

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ CFLAGS += -DCIRCUITPY_DISPLAYIO=$(CIRCUITPY_DISPLAYIO)
9494
CIRCUITPY_FRAMEBUFFERIO ?= 0
9595
CFLAGS += -DCIRCUITPY_FRAMEBUFFERIO=$(CIRCUITPY_FRAMEBUFFERIO)
9696

97+
CIRCUITPY_VECTORIO ?= 0
98+
CFLAGS += -DCIRCUITPY_VECTORIO=$(CIRCUITPY_VECTORIO)
99+
97100
CIRCUITPY_FREQUENCYIO ?= $(CIRCUITPY_FULL_BUILD)
98101
CFLAGS += -DCIRCUITPY_FREQUENCYIO=$(CIRCUITPY_FREQUENCYIO)
99102

shared-bindings/vectorio/Circle.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
2+
#include "shared-bindings/vectorio/Circle.h"
3+
4+
5+
#include <stdint.h>
6+
7+
#include "py/objproperty.h"
8+
#include "py/objtype.h"
9+
#include "py/runtime.h"
10+
#include "supervisor/shared/translate.h"
11+
12+
13+
//| .. currentmodule:: vectorio
14+
//|
15+
//| :class:`Circle` -- Represents a circle by its radius
16+
//| ==========================================================================
17+
//|
18+
//| .. class:: Circle(radius)
19+
//|
20+
//| :param int radius: The radius of the circle in pixels
21+
//|
22+
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) {
23+
enum { ARG_radius };
24+
static const mp_arg_t allowed_args[] = {
25+
{ MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_INT },
26+
};
27+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
28+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
29+
30+
mp_int_t radius = args[ARG_radius].u_int;
31+
if (radius < 1) {
32+
mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_radius);
33+
}
34+
35+
vectorio_circle_t *self = m_new_obj(vectorio_circle_t);
36+
self->base.type = &vectorio_circle_type;
37+
common_hal_vectorio_circle_construct(self, radius);
38+
39+
return MP_OBJ_FROM_PTR(self);
40+
}
41+
42+
43+
//| .. attribute:: radius
44+
//|
45+
//| Update the radius of the circle
46+
//|
47+
STATIC mp_obj_t vectorio_circle_obj_get_radius(mp_obj_t self_in) {
48+
vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in);
49+
return mp_obj_new_int(common_hal_vectorio_circle_get_radius(self));
50+
}
51+
MP_DEFINE_CONST_FUN_OBJ_1(vectorio_circle_get_radius_obj, vectorio_circle_obj_get_radius);
52+
53+
STATIC mp_obj_t vectorio_circle_obj_set_radius(mp_obj_t self_in, mp_obj_t radius) {
54+
vectorio_circle_t *self = MP_OBJ_TO_PTR(self_in);
55+
common_hal_vectorio_circle_set_radius(self, mp_obj_get_int(radius));
56+
return mp_const_none;
57+
}
58+
MP_DEFINE_CONST_FUN_OBJ_2(vectorio_circle_set_radius_obj, vectorio_circle_obj_set_radius);
59+
60+
const mp_obj_property_t vectorio_circle_radius_obj = {
61+
.base.type = &mp_type_property,
62+
.proxy = {(mp_obj_t)&vectorio_circle_get_radius_obj,
63+
(mp_obj_t)&vectorio_circle_set_radius_obj,
64+
(mp_obj_t)&mp_const_none_obj},
65+
};
66+
67+
68+
STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = {
69+
{ MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) },
70+
};
71+
STATIC MP_DEFINE_CONST_DICT(vectorio_circle_locals_dict, vectorio_circle_locals_dict_table);
72+
73+
const mp_obj_type_t vectorio_circle_type = {
74+
{ &mp_type_type },
75+
.name = MP_QSTR_Circle,
76+
.make_new = vectorio_circle_make_new,
77+
.locals_dict = (mp_obj_dict_t*)&vectorio_circle_locals_dict,
78+
};
79+

shared-bindings/vectorio/Circle.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H
2+
#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H
3+
4+
#include "shared-module/vectorio/__init__.h"
5+
#include "shared-module/vectorio/Circle.h"
6+
#include "shared-module/displayio/area.h"
7+
8+
extern const mp_obj_type_t vectorio_circle_type;
9+
10+
void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius);
11+
12+
void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t notification);
13+
14+
uint32_t common_hal_vectorio_circle_get_pixel(void *circle, int16_t x, int16_t y);
15+
16+
void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area);
17+
18+
19+
int16_t common_hal_vectorio_circle_get_radius(void *circle);
20+
void common_hal_vectorio_circle_set_radius(void *circle, int16_t radius);
21+
22+
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_CIRCLE_H

shared-bindings/vectorio/Polygon.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
2+
#include "shared-module/vectorio/__init__.h"
3+
#include "shared-bindings/vectorio/Polygon.h"
4+
5+
#include <stdint.h>
6+
7+
#include "py/obj.h"
8+
#include "py/objproperty.h"
9+
#include "py/objtype.h"
10+
#include "py/runtime.h"
11+
#include "supervisor/shared/translate.h"
12+
13+
14+
#define VECTORIO_POLYGON_DEBUG(...) (void)0
15+
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
16+
17+
18+
// Converts a list of points tuples to a flat list of ints for speedier internal use.
19+
// Also validates the points.
20+
static mp_obj_t _to_points_list(mp_obj_t points_tuple_list) {
21+
size_t len = 0;
22+
mp_obj_t *items;
23+
mp_obj_list_get(points_tuple_list, &len, &items);
24+
VECTORIO_POLYGON_DEBUG("polygon_points_list len: %d\n", len);
25+
26+
if ( len == 0 ) {
27+
mp_raise_TypeError_varg(translate("empty %q list"), MP_QSTR_point);
28+
}
29+
30+
mp_obj_t points_list = mp_obj_new_list(0, NULL);
31+
32+
for ( size_t i = 0; i < len; ++i) {
33+
size_t tuple_len = 0;
34+
mp_obj_t *tuple_items;
35+
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
36+
37+
if (tuple_len != 2) {
38+
mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point);
39+
}
40+
int value;
41+
if (!mp_obj_get_int_maybe(tuple_items[0], &value)) {
42+
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
43+
}
44+
mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value));
45+
if (!mp_obj_get_int_maybe(tuple_items[1], &value)) {
46+
mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point);
47+
}
48+
mp_obj_list_append(points_list, MP_OBJ_NEW_SMALL_INT(value));
49+
}
50+
return points_list;
51+
}
52+
53+
54+
55+
//| .. currentmodule:: vectorio
56+
//|
57+
//| :class:`Polygon` -- Represents a closed shape by ordered vertices
58+
//| ==========================================================================
59+
//|
60+
//| .. class:: Polygon( List[ Tuple[ x, y ], ... ] )
61+
//|
62+
//| :param [Point] points_array: Vertices for the polygon
63+
//|
64+
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) {
65+
enum { ARG_points_list };
66+
static const mp_arg_t allowed_args[] = {
67+
{ MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
68+
};
69+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
70+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
71+
72+
if (!MP_OBJ_IS_TYPE(args[ARG_points_list].u_obj, &mp_type_list)) {
73+
mp_raise_TypeError_varg(translate("%q list must be a list"), MP_QSTR_point);
74+
}
75+
mp_obj_t points_list = _to_points_list(args[ARG_points_list].u_obj);
76+
77+
vectorio_polygon_t *self = m_new_obj(vectorio_polygon_t);
78+
self->base.type = &vectorio_polygon_type;
79+
80+
common_hal_vectorio_polygon_construct(self, points_list);
81+
82+
return MP_OBJ_FROM_PTR(self);
83+
}
84+
85+
86+
//| .. attribute:: points
87+
//|
88+
//| Set a new look and shape for this polygon
89+
//|
90+
STATIC mp_obj_t vectorio_polygon_obj_get_points(mp_obj_t self_in) {
91+
vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in);
92+
mp_obj_t list = mp_obj_new_list(0, NULL);
93+
94+
size_t len = 0;
95+
mp_obj_t *items;
96+
mp_obj_list_get(common_hal_vectorio_polygon_get_points(self), &len, &items);
97+
98+
for (size_t i = 0; i < len; i += 2) {
99+
mp_obj_t tuple[] = { items[i], items[i+1] };
100+
mp_obj_list_append(
101+
list,
102+
mp_obj_new_tuple(2, tuple)
103+
);
104+
}
105+
return list;
106+
}
107+
MP_DEFINE_CONST_FUN_OBJ_1(vectorio_polygon_get_points_obj, vectorio_polygon_obj_get_points);
108+
109+
STATIC mp_obj_t vectorio_polygon_obj_set_points(mp_obj_t self_in, mp_obj_t points) {
110+
vectorio_polygon_t *self = MP_OBJ_TO_PTR(self_in);
111+
112+
mp_obj_t points_list = _to_points_list(points);
113+
114+
common_hal_vectorio_polygon_set_points(self, points_list);
115+
return mp_const_none;
116+
}
117+
MP_DEFINE_CONST_FUN_OBJ_2(vectorio_polygon_set_points_obj, vectorio_polygon_obj_set_points);
118+
119+
const mp_obj_property_t vectorio_polygon_points_obj = {
120+
.base.type = &mp_type_property,
121+
.proxy = {(mp_obj_t)&vectorio_polygon_get_points_obj,
122+
(mp_obj_t)&vectorio_polygon_set_points_obj,
123+
(mp_obj_t)&mp_const_none_obj},
124+
};
125+
126+
127+
128+
STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = {
129+
{ MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) },
130+
};
131+
STATIC MP_DEFINE_CONST_DICT(vectorio_polygon_locals_dict, vectorio_polygon_locals_dict_table);
132+
133+
const mp_obj_type_t vectorio_polygon_type = {
134+
{ &mp_type_type },
135+
.name = MP_QSTR_Polygon,
136+
.make_new = vectorio_polygon_make_new,
137+
.locals_dict = (mp_obj_dict_t*)&vectorio_polygon_locals_dict,
138+
};
139+

shared-bindings/vectorio/Polygon.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H
2+
#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H
3+
4+
#include "shared-module/vectorio/Polygon.h"
5+
#include "shared-module/displayio/area.h"
6+
#include "shared-module/vectorio/__init__.h"
7+
8+
extern const mp_obj_type_t vectorio_polygon_type;
9+
10+
void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list);
11+
void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification);
12+
13+
14+
uint32_t common_hal_vectorio_polygon_get_pixel(void *polygon, int16_t x, int16_t y);
15+
16+
void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *out_area);
17+
18+
19+
20+
mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self);
21+
void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list);
22+
23+
24+
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_POLYGON_H

shared-bindings/vectorio/Rectangle.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
#include "shared-bindings/vectorio/Rectangle.h"
3+
4+
#include <stdint.h>
5+
6+
#include "py/objtype.h"
7+
#include "py/runtime.h"
8+
#include "supervisor/shared/translate.h"
9+
10+
11+
//| .. currentmodule:: vectorio
12+
//|
13+
//| :class:`Rectangle` -- Represents a rectangle by defining its bounds
14+
//| ==========================================================================
15+
//|
16+
//| .. class:: Rectangle(width, height)
17+
//|
18+
//| :param int width: The number of pixels wide
19+
//| :param int height: The number of pixels high
20+
//|
21+
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) {
22+
enum { ARG_width, ARG_height };
23+
static const mp_arg_t allowed_args[] = {
24+
{ MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
25+
{ MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
26+
};
27+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
28+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
29+
30+
mp_int_t width = args[ARG_width].u_int;
31+
if (width < 1) {
32+
mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_width);
33+
}
34+
mp_int_t height = args[ARG_height].u_int;
35+
if (height < 1) {
36+
mp_raise_ValueError_varg(translate("%q must be >= 1"), MP_QSTR_height);
37+
}
38+
39+
vectorio_rectangle_t *self = m_new_obj(vectorio_rectangle_t);
40+
self->base.type = &vectorio_rectangle_type;
41+
common_hal_vectorio_rectangle_construct(self, width, height);
42+
43+
return MP_OBJ_FROM_PTR(self);
44+
}
45+
46+
47+
STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = {
48+
};
49+
STATIC MP_DEFINE_CONST_DICT(vectorio_rectangle_locals_dict, vectorio_rectangle_locals_dict_table);
50+
51+
const mp_obj_type_t vectorio_rectangle_type = {
52+
{ &mp_type_type },
53+
.name = MP_QSTR_Rectangle,
54+
.make_new = vectorio_rectangle_make_new,
55+
.locals_dict = (mp_obj_dict_t*)&vectorio_rectangle_locals_dict,
56+
};

shared-bindings/vectorio/Rectangle.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H
2+
#define MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H
3+
4+
#include "shared-module/vectorio/Rectangle.h"
5+
#include "shared-module/displayio/area.h"
6+
7+
extern const mp_obj_type_t vectorio_rectangle_type;
8+
9+
void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height);
10+
11+
uint32_t common_hal_vectorio_rectangle_get_pixel(void *rectangle, int16_t x, int16_t y);
12+
13+
void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area);
14+
15+
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_RECTANGLE_H

0 commit comments

Comments
 (0)