2020-05-02 05:21:35 -04:00
|
|
|
#include "shared-module/vectorio/__init__.h"
|
|
|
|
#include "shared-bindings/vectorio/Polygon.h"
|
|
|
|
#include "shared-module/displayio/area.h"
|
|
|
|
|
|
|
|
#include "py/runtime.h"
|
2020-05-13 00:55:18 -04:00
|
|
|
#include "py/gc.h"
|
|
|
|
|
2020-05-02 05:21:35 -04:00
|
|
|
#include "stdlib.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define VECTORIO_POLYGON_DEBUG(...) (void)0
|
2021-08-07 20:47:57 -04:00
|
|
|
// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print, __VA_ARGS__)
|
2020-05-02 05:21:35 -04:00
|
|
|
|
|
|
|
|
2020-05-13 00:55:18 -04:00
|
|
|
// Converts a list of points tuples to a flat list of ints for speedier internal use.
|
2022-06-23 09:36:22 -04:00
|
|
|
// Also validates the points. If this fails due to invalid types or values, the
|
2022-06-23 09:53:29 -04:00
|
|
|
// number of points is 0 and the points_list is NULL.
|
2020-05-13 00:55:18 -04:00
|
|
|
static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) {
|
|
|
|
size_t len = 0;
|
|
|
|
mp_obj_t *items;
|
|
|
|
mp_obj_list_get(points_tuple_list, &len, &items);
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" self.len: %d, len: %d, ", self->len, len);
|
2020-05-13 00:55:18 -04:00
|
|
|
|
2021-03-15 09:57:36 -04:00
|
|
|
if (len < 3) {
|
2021-09-10 04:34:20 -04:00
|
|
|
mp_raise_TypeError(translate("Polygon needs at least 3 points"));
|
2020-05-13 00:55:18 -04:00
|
|
|
}
|
|
|
|
|
2022-06-23 09:53:29 -04:00
|
|
|
int16_t *points_list = gc_realloc(self->points_list, 2 * len * sizeof(uint16_t), true);
|
|
|
|
VECTORIO_POLYGON_DEBUG("realloc(%p, %d) -> %p", self->points_list, 2 * len * sizeof(uint16_t), points_list);
|
|
|
|
|
|
|
|
// In case the validation calls below fail, set these values temporarily
|
|
|
|
self->points_list = NULL;
|
|
|
|
self->len = 0;
|
2020-05-13 00:55:18 -04:00
|
|
|
|
2021-08-07 20:47:57 -04:00
|
|
|
for (uint16_t i = 0; i < len; ++i) {
|
2020-05-13 00:55:18 -04:00
|
|
|
size_t tuple_len = 0;
|
|
|
|
mp_obj_t *tuple_items;
|
2023-05-08 16:39:19 -04:00
|
|
|
mp_arg_validate_type(items[i], &mp_type_tuple, MP_QSTR_point);
|
2020-05-13 00:55:18 -04:00
|
|
|
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
|
|
|
|
|
2022-05-13 15:33:43 -04:00
|
|
|
mp_arg_validate_length(tuple_len, 2, MP_QSTR_point);
|
|
|
|
|
2022-06-23 09:36:22 -04:00
|
|
|
mp_int_t x = mp_arg_validate_type_int(tuple_items[0], MP_QSTR_x);
|
|
|
|
mp_arg_validate_int_range(x, SHRT_MIN, SHRT_MAX, MP_QSTR_x);
|
|
|
|
mp_int_t y = mp_arg_validate_type_int(tuple_items[1], MP_QSTR_y);
|
|
|
|
mp_arg_validate_int_range(y, SHRT_MIN, SHRT_MAX, MP_QSTR_y);
|
2022-06-23 09:53:29 -04:00
|
|
|
points_list[2 * i ] = (int16_t)x;
|
|
|
|
points_list[2 * i + 1] = (int16_t)y;
|
2020-05-13 00:55:18 -04:00
|
|
|
}
|
2022-06-23 09:53:29 -04:00
|
|
|
|
|
|
|
self->points_list = points_list;
|
|
|
|
self->len = 2 * len;
|
2020-05-13 00:55:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-03-19 12:30:37 -04:00
|
|
|
void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list, uint16_t color_index) {
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("%p polygon_construct: ", self);
|
2020-05-13 00:55:18 -04:00
|
|
|
self->points_list = NULL;
|
|
|
|
self->len = 0;
|
2020-05-02 05:21:35 -04:00
|
|
|
self->on_dirty.obj = NULL;
|
2022-03-19 12:30:37 -04:00
|
|
|
self->color_index = color_index + 1;
|
2021-03-15 09:57:36 -04:00
|
|
|
_clobber_points_list(self, points_list);
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("\n");
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) {
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_points {len: %d, points_list: %p}\n", self, self->len, self->points_list);
|
2021-08-07 20:47:57 -04:00
|
|
|
mp_obj_list_t *list = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL));
|
|
|
|
|
|
|
|
VECTORIO_POLYGON_DEBUG(" >points\n");
|
|
|
|
for (uint16_t i = 0; i < self->len; i += 2) {
|
|
|
|
VECTORIO_POLYGON_DEBUG(" (%4d, %4d)\n", self->points_list[i], self->points_list[i + 1]);
|
|
|
|
|
|
|
|
mp_obj_tuple_t *pair = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
2021-08-07 22:40:07 -04:00
|
|
|
pair->items[0] = mp_obj_new_int((mp_int_t)self->points_list[i ]);
|
|
|
|
pair->items[1] = mp_obj_new_int((mp_int_t)self->points_list[i + 1]);
|
2020-07-19 15:27:35 -04:00
|
|
|
|
|
|
|
mp_obj_list_append(
|
|
|
|
list,
|
2021-08-07 20:47:57 -04:00
|
|
|
pair
|
2021-03-15 09:57:36 -04:00
|
|
|
);
|
2020-07-19 15:27:35 -04:00
|
|
|
}
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" <points\n");
|
|
|
|
return MP_OBJ_FROM_PTR(list);
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) {
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_set_points: ", self);
|
2021-03-15 09:57:36 -04:00
|
|
|
_clobber_points_list(self, points_list);
|
2020-05-02 05:21:35 -04:00
|
|
|
if (self->on_dirty.obj != NULL) {
|
|
|
|
self->on_dirty.event(self->on_dirty.obj);
|
|
|
|
}
|
2020-07-19 15:27:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("\n");
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification) {
|
2021-03-15 09:57:36 -04:00
|
|
|
if (self->on_dirty.obj != NULL) {
|
2022-05-13 15:33:43 -04:00
|
|
|
mp_raise_TypeError(translate("can only have one parent"));
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
self->on_dirty = notification;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) {
|
|
|
|
vectorio_polygon_t *self = polygon;
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_area\n");
|
2020-05-02 05:21:35 -04:00
|
|
|
|
|
|
|
area->x1 = SHRT_MAX;
|
|
|
|
area->y1 = SHRT_MAX;
|
|
|
|
area->x2 = SHRT_MIN;
|
|
|
|
area->y2 = SHRT_MIN;
|
2021-08-07 20:47:57 -04:00
|
|
|
for (uint16_t i = 0; i < self->len; ++i) {
|
|
|
|
int16_t x = self->points_list[i];
|
2020-05-02 05:21:35 -04:00
|
|
|
++i;
|
2021-08-07 20:47:57 -04:00
|
|
|
int16_t y = self->points_list[i];
|
2021-08-01 16:01:57 -04:00
|
|
|
if (x < area->x1) {
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" x1: %d\n", x);
|
2021-08-01 16:01:57 -04:00
|
|
|
area->x1 = x;
|
2021-03-15 09:57:36 -04:00
|
|
|
}
|
2021-08-01 16:01:57 -04:00
|
|
|
if (y < area->y1) {
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" y1: %d\n", y);
|
2021-08-01 16:01:57 -04:00
|
|
|
area->y1 = y;
|
2021-03-15 09:57:36 -04:00
|
|
|
}
|
2021-08-01 16:01:57 -04:00
|
|
|
if (x > area->x2) {
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" x2: %d\n", x);
|
2021-08-01 16:01:57 -04:00
|
|
|
area->x2 = x;
|
2021-03-15 09:57:36 -04:00
|
|
|
}
|
2021-08-01 16:01:57 -04:00
|
|
|
if (y > area->y2) {
|
2021-08-07 20:47:57 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" y2: %d\n", y);
|
2021-08-01 16:01:57 -04:00
|
|
|
area->y2 = y;
|
2021-03-15 09:57:36 -04:00
|
|
|
}
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <0 if the point is to the left of the line vector
|
|
|
|
// 0 if the point is on the line
|
|
|
|
// >0 if the point is to the right of the line vector
|
2021-08-07 20:47:57 -04:00
|
|
|
__attribute__((always_inline)) static inline int line_side(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t px, int16_t py) {
|
2020-05-02 05:21:35 -04:00
|
|
|
return (px - x1) * (y2 - y1)
|
2021-03-15 09:57:36 -04:00
|
|
|
- (py - y1) * (x2 - x1);
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) {
|
|
|
|
VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y);
|
|
|
|
vectorio_polygon_t *self = obj;
|
|
|
|
|
2020-05-13 00:55:18 -04:00
|
|
|
if (self->len == 0) {
|
2020-05-02 05:21:35 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-07 20:47:57 -04:00
|
|
|
int16_t winding_number = 0;
|
|
|
|
int16_t x1 = self->points_list[0];
|
|
|
|
int16_t y1 = self->points_list[1];
|
|
|
|
for (uint16_t i = 2; i <= self->len + 1; ++i) {
|
2020-05-02 05:21:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1);
|
2021-08-07 20:47:57 -04:00
|
|
|
int16_t x2 = self->points_list[i % self->len];
|
2020-05-02 05:21:35 -04:00
|
|
|
++i;
|
2021-08-07 20:47:57 -04:00
|
|
|
int16_t y2 = self->points_list[i % self->len];
|
2020-05-02 05:21:35 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2);
|
2021-03-15 09:57:36 -04:00
|
|
|
if (y1 <= y) {
|
|
|
|
if (y2 > y && line_side(x1, y1, x2, y2, x, y) < 0) {
|
use top-left heuristic for vectorio.Polygon
this flips the bottom-right style to top-left which is at least
kind of normal. A 2x2 square at (0,0) would be defined like
(0,0), (3,0), (3,3), (0,3)
Which seems kind of surprising but at least less bonkers than
that square being defined at (1,1), which is the current behavior.
2020-07-29 01:58:28 -04:00
|
|
|
// Wind up, point is to the left of the edge vector
|
2020-05-02 05:21:35 -04:00
|
|
|
++winding_number;
|
|
|
|
VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", 1, winding_number);
|
|
|
|
}
|
2021-03-15 09:57:36 -04:00
|
|
|
} else if (y2 <= y && line_side(x1, y1, x2, y2, x, y) > 0) {
|
use top-left heuristic for vectorio.Polygon
this flips the bottom-right style to top-left which is at least
kind of normal. A 2x2 square at (0,0) would be defined like
(0,0), (3,0), (3,3), (0,3)
Which seems kind of surprising but at least less bonkers than
that square being defined at (1,1), which is the current behavior.
2020-07-29 01:58:28 -04:00
|
|
|
// Wind down, point is to the right of the edge vector
|
2020-05-02 05:21:35 -04:00
|
|
|
--winding_number;
|
2020-05-30 05:44:13 -04:00
|
|
|
VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", -1, winding_number);
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
x1 = x2;
|
|
|
|
y1 = y2;
|
|
|
|
}
|
2022-03-19 12:30:37 -04:00
|
|
|
return winding_number == 0 ? 0 : self->color_index;
|
2020-05-02 05:21:35 -04:00
|
|
|
}
|
2021-08-01 16:01:57 -04:00
|
|
|
|
|
|
|
mp_obj_t common_hal_vectorio_polygon_get_draw_protocol(void *polygon) {
|
|
|
|
vectorio_polygon_t *self = polygon;
|
|
|
|
return self->draw_protocol_instance;
|
|
|
|
}
|
2022-03-19 12:30:37 -04:00
|
|
|
|
|
|
|
uint16_t common_hal_vectorio_polygon_get_color_index(void *obj) {
|
|
|
|
vectorio_polygon_t *self = obj;
|
|
|
|
return self->color_index - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void common_hal_vectorio_polygon_set_color_index(void *obj, uint16_t color_index) {
|
|
|
|
vectorio_polygon_t *self = obj;
|
|
|
|
self->color_index = abs(color_index + 1);
|
|
|
|
if (self->on_dirty.obj != NULL) {
|
|
|
|
self->on_dirty.event(self->on_dirty.obj);
|
|
|
|
}
|
|
|
|
}
|