vectorio contains(x, y)

new utility function for all vectorio shape specializations for testing
whether a screen-space x,y point falls within a shape's x,y.

This respects the current orientation of the screen in the manner of
displayio and vectorio - so your x,y requests are in the same coordinate
domain as your x,y locations and your width/height etc. properties that
ou set on other shapes. I.e., if you're using this for touch points then
you will need to make sure the touch events are in the same x,y domain as
your display.

```
contains(2, 4) -> true
------------------
|                |
|                |
| --             |
| | \            |
| |. \           |
| |   \          |
| |____\         |
|                |
------------------

contains(5, 4) -> false
------------------
|                |
|                |
| --             |
| | \            |
| |  \.          |
| |   \          |
| |____\         |
|                |
------------------
```

This helps provide low overhead introspection of shape coverage on screen.
It's envisioned that this will be used for things like touch-and-drag
widget controls, touch "areas" and may help with random ornament placement
on toy Christmas trees.
This commit is contained in:
Kenny 2021-12-27 11:31:18 -10:00
parent f5fa4ae237
commit 8de5f90086
6 changed files with 82 additions and 45 deletions

View File

@ -97,6 +97,8 @@ const mp_obj_property_t vectorio_circle_radius_obj = {
//| //|
STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = { STATIC const mp_rom_map_elem_t vectorio_circle_locals_dict_table[] = {
// Functions
{ MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) }, { MP_ROM_QSTR(MP_QSTR_radius), MP_ROM_PTR(&vectorio_circle_radius_obj) },
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) },

View File

@ -103,6 +103,8 @@ const mp_obj_property_t vectorio_polygon_points_obj = {
//| //|
STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = { STATIC const mp_rom_map_elem_t vectorio_polygon_locals_dict_table[] = {
// Functions
{ MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) }, { MP_ROM_QSTR(MP_QSTR_points), MP_ROM_PTR(&vectorio_polygon_points_obj) },
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) },

View File

@ -122,6 +122,8 @@ const mp_obj_property_t vectorio_rectangle_height_obj = {
//| //|
STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = { STATIC const mp_rom_map_elem_t vectorio_rectangle_locals_dict_table[] = {
// Functions
{ MP_ROM_QSTR(MP_QSTR_contains), MP_ROM_PTR(&vectorio_vector_shape_contains_obj) },
// Properties // Properties
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&vectorio_vector_shape_x_obj) }, { 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_y), MP_ROM_PTR(&vectorio_vector_shape_y_obj) },

View File

@ -80,6 +80,21 @@ vectorio_draw_protocol_impl_t vectorio_vector_shape_draw_protocol_impl = {
.draw_get_refresh_areas = (draw_get_refresh_areas_fun)vectorio_vector_shape_get_refresh_areas, .draw_get_refresh_areas = (draw_get_refresh_areas_fun)vectorio_vector_shape_get_refresh_areas,
}; };
// Stub checker does not approve of these shared properties.
// x: int
// y: int
// """true if x,y lies inside the shape."""
//
STATIC mp_obj_t vectorio_vector_shape_obj_contains(mp_obj_t wrapper_shape, mp_obj_t x_obj, mp_obj_t y_obj) {
// Relies on the fact that only vector_shape impl gets matched with a VectorShape.
const vectorio_draw_protocol_t *draw_protocol = mp_proto_get(MP_QSTR_protocol_draw, wrapper_shape);
vectorio_vector_shape_t *self = MP_OBJ_TO_PTR(draw_protocol->draw_get_protocol_self(wrapper_shape));
mp_int_t x = mp_obj_get_int(x_obj);
mp_int_t y = mp_obj_get_int(y_obj);
return mp_obj_new_bool(common_hal_vectorio_vector_shape_contains(self, x, y));
}
MP_DEFINE_CONST_FUN_OBJ_3(vectorio_vector_shape_contains_obj, vectorio_vector_shape_obj_contains);
// Stub checker does not approve of these shared properties. // Stub checker does not approve of these shared properties.
// x: int // x: int

View File

@ -18,6 +18,8 @@ void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self,
vectorio_ishape_t ishape, vectorio_ishape_t ishape,
mp_obj_t pixel_shader, int32_t x, int32_t y); mp_obj_t pixel_shader, int32_t x, int32_t y);
bool common_hal_vectorio_vector_shape_contains(vectorio_vector_shape_t *self, mp_int_t x, mp_int_t y);
void common_hal_vectorio_vector_shape_set_dirty(void *self); 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); mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self);
@ -40,5 +42,6 @@ extern const mp_obj_property_t vectorio_vector_shape_x_obj;
extern const mp_obj_property_t vectorio_vector_shape_y_obj; extern const mp_obj_property_t vectorio_vector_shape_y_obj;
extern const mp_obj_property_t vectorio_vector_shape_location_obj; extern const mp_obj_property_t vectorio_vector_shape_location_obj;
extern const mp_obj_property_t vectorio_vector_shape_pixel_shader_obj; extern const mp_obj_property_t vectorio_vector_shape_pixel_shader_obj;
extern const mp_obj_fun_builtin_fixed_t vectorio_vector_shape_contains_obj;
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_VECTORIO_SHAPE_H

View File

@ -72,6 +72,12 @@ static uint32_t min(uint32_t a, uint32_t b) {
return a < b ? a : b; return a < b ? a : b;
} }
static void short_bound_check(mp_int_t i, qstr name) {
if (i < SHRT_MIN || i > SHRT_MAX) {
mp_raise_ValueError_varg(translate("%q must be between %d and %d"), name, SHRT_MIN, SHRT_MAX);
}
}
inline __attribute__((always_inline)) inline __attribute__((always_inline))
static void area_transpose(displayio_area_t *to_transpose) { static void area_transpose(displayio_area_t *to_transpose) {
int16_t swap = to_transpose->y1; int16_t swap = to_transpose->y1;
@ -124,21 +130,50 @@ static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *ou
VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2); VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2);
} }
// Get the target pixel based on the shape's coordinate space
static void screen_to_shape_coordinates(vectorio_vector_shape_t *self, uint16_t x, uint16_t y, int16_t *out_shape_x, int16_t *out_shape_y) {
if (self->absolute_transform->transpose_xy) {
*out_shape_x = y - self->absolute_transform->y - self->absolute_transform->dy * self->x;
*out_shape_y = x - self->absolute_transform->x - self->absolute_transform->dx * self->y;
STATIC VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", *out_shape_x, *out_shape_y);
void check_bounds_and_set_x(vectorio_vector_shape_t *self, mp_int_t x) { if (self->absolute_transform->dx < 1) {
if (x < SHRT_MIN || x > SHRT_MAX) { *out_shape_y *= -1;
mp_raise_ValueError_varg(translate("%q must be between %d and %d"), MP_QSTR_x, SHRT_MIN, SHRT_MAX); }
if (self->absolute_transform->dy < 1) {
*out_shape_x *= -1;
}
VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", *out_shape_x, *out_shape_y);
} else {
*out_shape_x = x - self->absolute_transform->x - self->absolute_transform->dx * self->x;
*out_shape_y = y - self->absolute_transform->y - self->absolute_transform->dy * self->y;
VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", *out_shape_x, *out_shape_y);
if (self->absolute_transform->dx < 1) {
*out_shape_x *= -1;
}
if (self->absolute_transform->dy < 1) {
*out_shape_y *= -1;
}
VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", *out_shape_x, *out_shape_y);
// It's mirrored via dx. Maybe we need to add support for also separately mirroring?
// if (self->absolute_transform->mirror_x) {
// pixel_to_get_x = (shape_area.x2 - shape_area.x1) - (pixel_to_get_x - shape_area.x1) + shape_area.x1 - 1;
// }
// if (self->absolute_transform->mirror_y) {
// pixel_to_get_y = (shape_area.y2 - shape_area.y1) - (pixel_to_get_y - shape_area.y1) + +shape_area.y1 - 1;
// }
} }
}
static void check_bounds_and_set_x(vectorio_vector_shape_t *self, mp_int_t x) {
short_bound_check(x, MP_QSTR_x);
self->x = x; self->x = x;
} }
static void check_bounds_and_set_y(vectorio_vector_shape_t *self, mp_int_t y) {
STATIC short_bound_check(y, MP_QSTR_y);
void check_bounds_and_set_y(vectorio_vector_shape_t *self, mp_int_t y) {
if (y < SHRT_MIN || y > SHRT_MAX) {
mp_raise_ValueError_varg(translate("%q must be between %d and %d"), MP_QSTR_y, SHRT_MIN, SHRT_MAX);
}
self->y = y; self->y = y;
} }
@ -195,6 +230,17 @@ void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self,
_get_screen_area(self, &self->current_area); _get_screen_area(self, &self->current_area);
} }
bool common_hal_vectorio_vector_shape_contains(vectorio_vector_shape_t *self, mp_int_t x, mp_int_t y) {
VECTORIO_SHAPE_DEBUG("%p contains(%d, %d)", self);
short_bound_check(x, MP_QSTR_x);
short_bound_check(y, MP_QSTR_y);
int16_t shape_x;
int16_t shape_y;
screen_to_shape_coordinates(self, x, y, &shape_x, &shape_y);
bool shape_contains_coordinates = 0 != self->ishape.get_pixel(self->ishape.shape, shape_x, shape_y);
return shape_contains_coordinates;
}
mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self) { mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self) {
VECTORIO_SHAPE_DEBUG("%p get_x\n", self); VECTORIO_SHAPE_DEBUG("%p get_x\n", self);
@ -277,7 +323,6 @@ void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *
common_hal_vectorio_vector_shape_set_dirty(self); common_hal_vectorio_vector_shape_set_dirty(self);
} }
bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer) { bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer) {
// Shape areas are relative to 0,0. This will allow rotation about a known axis. // Shape areas are relative to 0,0. This will allow rotation about a known axis.
// The consequence is that the area reported by the shape itself is _relative_ to 0,0. // The consequence is that the area reported by the shape itself is _relative_ to 0,0.
@ -335,42 +380,10 @@ bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displ
} }
output_pixel.pixel = 0; output_pixel.pixel = 0;
// Get the target pixel based on the shape's coordinate space // Cast input screen coordinates to shape coordinates to pick the pixel to draw
int16_t pixel_to_get_x; int16_t pixel_to_get_x;
int16_t pixel_to_get_y; int16_t pixel_to_get_y;
if (self->absolute_transform->transpose_xy) { screen_to_shape_coordinates(self, input_pixel.x, input_pixel.y, &pixel_to_get_x, &pixel_to_get_y);
pixel_to_get_x = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->x;
pixel_to_get_y = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->y;
VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", pixel_to_get_x, pixel_to_get_y);
if (self->absolute_transform->dx < 1) {
pixel_to_get_y *= -1;
}
if (self->absolute_transform->dy < 1) {
pixel_to_get_x *= -1;
}
VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", pixel_to_get_x, pixel_to_get_y);
} else {
pixel_to_get_x = input_pixel.x - self->absolute_transform->x - self->absolute_transform->dx * self->x;
pixel_to_get_y = input_pixel.y - self->absolute_transform->y - self->absolute_transform->dy * self->y;
VECTORIO_SHAPE_PIXEL_DEBUG(" a(%3d, %3d)", pixel_to_get_x, pixel_to_get_y);
if (self->absolute_transform->dx < 1) {
pixel_to_get_x *= -1;
}
if (self->absolute_transform->dy < 1) {
pixel_to_get_y *= -1;
}
VECTORIO_SHAPE_PIXEL_DEBUG(" b(%3d, %3d)", pixel_to_get_x, pixel_to_get_y);
// It's mirrored via dx. Maybe we need to add support for also separately mirroring?
// if (self->absolute_transform->mirror_x) {
// pixel_to_get_x = (shape_area.x2 - shape_area.x1) - (pixel_to_get_x - shape_area.x1) + shape_area.x1 - 1;
// }
// if (self->absolute_transform->mirror_y) {
// pixel_to_get_y = (shape_area.y2 - shape_area.y1) - (pixel_to_get_y - shape_area.y1) + +shape_area.y1 - 1;
// }
}
VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y);
#ifdef VECTORIO_PERF #ifdef VECTORIO_PERF