Mercurial > MadButterfly
changeset 232:527894c2ad39
Add functions for collision test.
- mb_obj_pos_is_in() test if two coords and their descendants are overlaid.
- mb_objs_is_overlay() test if a point is covered by another mb_obj_t.
author | Thinker K.F. Li <thinker@branda.to> |
---|---|
date | Sun, 21 Dec 2008 23:30:00 +0800 |
parents | 2637519e2bd7 |
children | ec62453bbb2b |
files | include/mb_redraw_man.h include/mb_types.h src/event.c |
diffstat | 3 files changed, 291 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/include/mb_redraw_man.h Thu Dec 18 22:37:15 2008 +0800 +++ b/include/mb_redraw_man.h Sun Dec 21 23:30:00 2008 +0800 @@ -165,6 +165,10 @@ extern shape_t *find_shape_at_pos(redraw_man_t *rdman, co_aix x, co_aix y, int *in_stroke); +extern int mb_obj_pos_is_in(redraw_man_t *rdman, mb_obj_t *obj, + co_aix x, co_aix y, int *in_stroke); +extern int mb_objs_is_overlay(redraw_man_t *rdman, + mb_obj_t *obj1, mb_obj_t *obj2); #define rdman_get_ob_factory(rdman) (&(rdman)->ob_factory) #define rdman_get_redraw_subject(rdman) ((rdman)->redraw) #define rdman_get_root(rdman) ((rdman)->root_coord) @@ -182,4 +186,5 @@ */ void sprite_set_search_path(char *path); + #endif /* __REDRAW_MAN_H_ */
--- a/include/mb_types.h Thu Dec 18 22:37:15 2008 +0800 +++ b/include/mb_types.h Sun Dec 21 23:30:00 2008 +0800 @@ -55,6 +55,7 @@ #define mb_obj_destroy(obj) #define mb_obj_prop_store(obj) (&(obj)->props) + /* @} */ /*! \brief Base of paint types. @@ -103,6 +104,7 @@ #define GEF_DIRTY 0x1 #define GEF_HIDDEN 0x2 /*!< The geo is hidden. */ #define GEF_FREE 0x4 +#define GEF_OV_DRAW 0x8 /*!< To flag drawed for a overlay testing. */ extern int is_overlay(area_t *r1, area_t *r2); extern void area_init(area_t *area, int n_pos, co_aix pos[][2]); @@ -116,6 +118,7 @@ #define geo_pos_is_in(g, _x, _y) \ (_geo_is_in(_x, (g)->cur_area->x, (g)->cur_area->w) && \ _geo_is_in(_y, (g)->cur_area->y, (g)->cur_area->h)) +#define geo_get_area(g) ((g)->cur_area) /*! \brief A coordination system. @@ -181,7 +184,6 @@ extern void update_aggr_matrix(coord_t *start); extern coord_t *preorder_coord_subtree(coord_t *root, coord_t *last); extern coord_t *postorder_coord_subtree(coord_t *root, coord_t *last); -extern void preorder_coord_skip_subtree(coord_t *subroot); #define preorder_coord_skip_subtree(sub) \ do { (sub)->flags |= COF_SKIP_TRIVAL; } while(0) #define coord_hide(co) \ @@ -190,6 +192,14 @@ } while(0) #define coord_show(co) do { co->flags &= ~COF_HIDDEN; } while(0) #define coord_get_mouse_event(coord) ((coord)->mouse_event) +#define FOR_COORDS_POSTORDER(coord, cur) \ + for((cur) = postorder_coord_subtree((coord), NULL); \ + (cur) != NULL; \ + (cur) = postorder_coord_subtree((coord), (cur))) +#define FOR_COORDS_PREORDER(coord, cur) \ + for((cur) = (coord); \ + (cur) != NULL; \ + (cur) = preorder_coord_subtree((coord), (cur))) /*! \brief Coord operation function * These functions are used to move and scale the coord_t. Programmers should use these functions instead of using the matrix directly. @@ -203,6 +213,11 @@ #define coord_scaley(ci) ((co)->matrix[3]) #define coord_x(ci) ((co)->matrix[2]) #define coord_y(ci) ((co)->matrix[5]) +#define FOR_COORD_MEMBERS(coord, geo) \ + for(geo = STAILQ_HEAD((coord)->members); \ + geo != NULL; \ + geo = STAILQ_NEXT(geo_t, coord_next, geo)) +#define coord_get_area(coord) ((coord)->cur_area) /*! \brief A grahpic shape. * @@ -238,6 +253,7 @@ do { \ (sh)->geo->flags &= ~GEF_HIDDEN; \ } while(0) +#define sh_get_geo(sh) ((sh)->geo) /*! \brief A sprite is a set of graphics that being an object in animation.
--- a/src/event.c Thu Dec 18 22:37:15 2008 +0800 +++ b/src/event.c Sun Dec 21 23:30:00 2008 +0800 @@ -1,3 +1,6 @@ +/*! \file + * \brief Convenience functions for event relative work. + */ #include <stdio.h> #include <stdlib.h> #include <cairo.h> @@ -7,6 +10,8 @@ #define OK 0 #define ERR -1 +#define FALSE 0 +#define TRUE 1 #define ARRAY_EXT_SZ 64 @@ -20,14 +25,14 @@ * different modules that need to select part of geo_t objects * from a redraw manager. */ -static int add_gen_geo(redraw_man_t *rdman, geo_t *geo) { +static int _add_gen_geo(redraw_man_t *rdman, geo_t *geo) { int r; r = geos_add(&rdman->gen_geos, geo); return r == 0? OK: ERR; } -static int collect_shapes_at_point(redraw_man_t *rdman, +static int _collect_geos_at_point(redraw_man_t *rdman, co_aix x, co_aix y) { geo_t *geo; int r; @@ -42,7 +47,7 @@ geo != NULL; geo = rdman_geos(rdman, geo)) { if(geo_pos_is_in(geo, x, y)) { - r = add_gen_geo(rdman, geo); + r = _add_gen_geo(rdman, geo); if(r != OK) return ERR; } @@ -65,13 +70,31 @@ } } +static int _shape_pos_is_in_cairo(shape_t *shape, co_aix x, co_aix y, + int *in_stroke, cairo_t *cr) { + draw_shape_path(shape, cr); + if(shape->fill) { + if(cairo_in_fill(cr, x, y)) { + *in_stroke = 0; + return TRUE; + } + } + if(shape->stroke) { + if(cairo_in_stroke(cr, x, y)) { + *in_stroke = 1; + return TRUE; + } + } + return FALSE; +} + static geo_t *find_geo_in_pos(redraw_man_t *rdman, co_aix x, co_aix y, int *in_stroke) { geo_t *geo; geo_t **geos; shape_t *shape; cairo_t *cr; - int i; + int i, r; geos = rdman->gen_geos.ds; cr = rdman->cr; @@ -79,23 +102,11 @@ geo = geos[i]; if(geo->flags & GEF_HIDDEN) continue; - shape = geo->shape; - draw_shape_path(shape, cr); - if(shape->fill) { - if(cairo_in_fill(cr, x, y)) { - *in_stroke = 0; - cairo_new_path(rdman->cr); - return geo; - } - } - if(shape->stroke) { - if(cairo_in_stroke(cr, x, y)) { - *in_stroke = 1; - cairo_new_path(rdman->cr); - return geo; - } - } - cairo_new_path(rdman->cr); + shape = geo_get_shape(geo); + r = _shape_pos_is_in_cairo(shape, x, y, in_stroke, cr); + cairo_new_path(cr); + if(r) + return geo; } return NULL; @@ -106,7 +117,7 @@ geo_t *geo; int r; - r = collect_shapes_at_point(rdman, x, y); + r = _collect_geos_at_point(rdman, x, y); if(r != OK) return NULL; @@ -114,5 +125,240 @@ if(geo == NULL) return NULL; - return geo->shape; + return geo_get_shape(geo); +} + +static +int _shape_pos_is_in(redraw_man_t *rdman, shape_t *shape, + co_aix x, co_aix y, int *in_stroke) { + geo_t *geo; + int r; + + geo = sh_get_geo(shape); + r = geo_pos_is_in(geo, x, y); + if(!r) + return FALSE; + + r = _shape_pos_is_in_cairo(shape, x, y, in_stroke, rdman->cr); + if(!r) + return FALSE; + + return TRUE; +} + +/*! \brief Test if an object and descendants cover the position + * specified by x,y. + * + * \param in_stroke is x, y is on a stroke. + */ +int mb_obj_pos_is_in(redraw_man_t *rdman, mb_obj_t *obj, + co_aix x, co_aix y, int *in_stroke) { + coord_t *cur_coord, *root; + shape_t *shape; + geo_t *geo; + int r; + + if(IS_MBO_SHAPES(obj)) { + shape = (shape_t *)obj; + r = _shape_pos_is_in_cairo(shape, x, y, in_stroke, rdman->cr); + return r; + } + root = (coord_t *)obj; + for(cur_coord = postorder_coord_subtree(root, NULL); + cur_coord != NULL; + cur_coord = postorder_coord_subtree(root, cur_coord)) { + FOR_COORD_MEMBERS(cur_coord, geo) { + shape = geo_get_shape(geo); + r = _shape_pos_is_in_cairo(shape, x, y, in_stroke, rdman->cr); + if(r) + return TRUE; + } + } + return FALSE; +} + +static +cairo_t * _prepare_cairo_for_testing(redraw_man_t *rdman) { + cairo_surface_t *surface, *rdman_surface; + cairo_t *cr; + int w, h; + + rdman_surface = cairo_get_target(rdman->cr); + w = cairo_image_surface_get_width(rdman_surface); + h = cairo_image_surface_get_height(rdman_surface); + + surface = cairo_image_surface_create(CAIRO_FORMAT_A1, w, h); + if(surface == NULL) + return NULL; + + cr = cairo_create(surface); + if(cr == NULL) + cairo_surface_destroy(surface); + + return cr; +} + +static +void _release_cairo_for_testing(cairo_t *cr) { + cairo_destroy(cr); +} + +static _draw_to_mask(shape_t *shape, cairo_t *cr) { + geo_t *geo; + + geo = sh_get_geo(shape); + if(geo->flags & GEF_OV_DRAW) + return; + + draw_shape_path(shape, cr); + cairo_clip(cr); + + geo->flags |= GEF_OV_DRAW; +} + +static +int _fill_and_check(shape_t *shape, cairo_t *cr) { + int h, stride; + cairo_surface_t *surface; + unsigned char *data; + int i, sz; + + draw_shape_path(shape, cr); + cairo_fill(cr); + + surface = cairo_get_target(cr); + data = cairo_image_surface_get_data(surface); + + h = cairo_image_surface_get_height(surface); + stride = cairo_image_surface_get_stride(surface); + + sz = stride * h; + for(i = 0; i < sz; i++) { + if(data[i]) + return TRUE; + } + + return FALSE; } + +/*! \brief Is a mb_obj_t overlaid with another mb_object_t and + * descendants. + * + * coord is relative less than shapes. Check areas of coord can + * havily avoid useless computation. For shapes, it not only check + * overlay of area. It also check overlay by actually drawing on a + * cairo surface. + */ +static +int _is_obj_objs_overlay(mb_obj_t *obj, mb_obj_t *others_root, + cairo_t *cr) { + area_t *area, *candi_area; + coord_t *coord, *candi_coord, *root; + shape_t *shape, *candi_shape; + geo_t *geo, *candi_geo; + int obj_is_shape; + int r; + /* + */ + + obj_is_shape = IS_MBO_SHAPES(obj); + + if(obj_is_shape) { + shape = (shape_t *)obj; + geo = sh_get_geo(shape); + area = geo_get_area(geo); + } else { + coord = (coord_t *)obj; + area = coord_get_area(coord); + } + + if(IS_MBO_SHAPES(others_root)) { + candi_shape = (shape_t *)others_root; + candi_geo = sh_get_geo(candi_shape); + candi_area = geo_get_area(candi_geo); + + r = is_overlay(area, candi_area); + if(!r) + return FALSE; + + if(!obj_is_shape) + return TRUE; + + _draw_to_mask(candi_shape, cr); + r = _fill_and_check(shape, cr); + + return r; + } + + root = (coord_t *)others_root; + FOR_COORDS_PREORDER(root, candi_coord) { + candi_area = coord_get_area(candi_coord); + r = is_overlay(area, candi_area); + if(!r) { + preorder_coord_skip_subtree(coord); + continue; + } + + FOR_COORD_MEMBERS(coord, candi_geo) { + candi_area = geo_get_area(candi_geo); + r = is_overlay(area, candi_area); + if(!r) + continue; + + if(!obj_is_shape) + return TRUE; + + _draw_to_mask(candi_shape, cr); + r = _fill_and_check(shape, cr); + if(r) + return TRUE; + } + } + + return FALSE; +} + +/*! \brief Test if two objects are overlaid. + * + * \todo Detect overlay in better way with cairo. + * \note This function cost heavy on CPU power. + */ +int mb_objs_is_overlay(redraw_man_t *rdman, + mb_obj_t *obj1, mb_obj_t *obj2) { + cairo_t *cr; + area_t *area; + shape_t *shape; + geo_t *geo; + coord_t *coord, *root; + int r; + + cr = _prepare_cairo_for_testing(rdman); + + if(IS_MBO_SHAPES(obj1)) { + shape = (shape_t *)obj1; + r = _is_obj_objs_overlay(obj1, obj2, cr); + goto out; + } + + root = (coord_t *)obj1; + FOR_COORDS_PREORDER(root, coord) { + area = coord_get_area(coord); + r = _is_obj_objs_overlay((mb_obj_t *)coord, obj2, cr); + if(!r) { + preorder_coord_skip_subtree(coord); + continue; + } + + FOR_COORD_MEMBERS(coord, geo) { + shape = geo_get_shape(geo); + r = _is_obj_objs_overlay((mb_obj_t *)shape, obj2, cr); + if(r) + goto out; + } + } + r = FALSE; + + out: + _release_cairo_for_testing(cr); + return r; +}