diff --git a/docs/config.rst b/docs/config.rst index 14904a45a..0ad6f3c07 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1195,6 +1195,28 @@ BiomeOverlay BiomeOverlay(biomes=[("Forest", (0, 255, 0)), ("Desert", (255, 0, 0))]) +BoundsOverlay + Color the map according to a set of bounding boxes. With this boundaries of + clans, land ownership, towns, etc. can be realized. + + This Overlay colors according to a patterns that are specified as + multiple tuples of the form ``(minx, minz, maxx, maxz)``. So + by specifying ``(0, 0, 16, 16)`` all the blocks (inclusive) within the boundary + created by these coordinates will be colored. The color is then specified as ``(r, g, b, a)``. + + Example:: + + BoundsOverlay(bounds=[ + (((0, 0, 16, 16),), (255, 160, 122, 255)), + (((17, 17, 23, 23),(24, 24, 40, 40)), (75, 0, 130, 255)), + ]) + + In this example the blocks within (0, 0, 16, 16) will be red and + all blocks within both (17, 17, 23, 23) and (24, 24, 40, 40) will be blue. + + Note the trailing comma behind the tuple, this is required if only one bounding + box is given. + Defining Custom Rendermodes --------------------------- diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index 00cac4a9c..a5656d0e6 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -216,7 +216,12 @@ class StructureOverlay(Overlay): (((0, 0, 0, 157), (0, -1, 0, 4)), (255, 100, 0, 255)), ]), } - + +class BoundsOverlay(Overlay): + name = "overlay-bounds" + options = { + 'bounds': ('a list of (((minx, minz, maxx, maxz), ...), (r, g, b, a)) tuples for coloring boundaries', None), + } class MineralOverlay(Overlay): name = "overlay-mineral" diff --git a/overviewer_core/src/primitives/overlay-bounds.c b/overviewer_core/src/primitives/overlay-bounds.c new file mode 100644 index 000000000..d543346b5 --- /dev/null +++ b/overviewer_core/src/primitives/overlay-bounds.c @@ -0,0 +1,249 @@ +/* + * This file is part of the Minecraft Overviewer. + * + * Minecraft Overviewer is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Minecraft Overviewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Overviewer. If not, see . + */ + +#include "overlay.h" + +typedef enum { false, true } bool; + +typedef struct { + /* inherits from overlay */ + RenderPrimitiveOverlay parent; + void *bounds; + int numcolors; +} RenderPrimitiveBounds; + +struct Condition{ + int minx, minz, maxx, maxz; +}; + + +struct Color { + int numconds; + struct Condition *conditions; + unsigned char r, g, b, a; +}; + +/* +* Determines if point x,z is inside bounds minx, minz, maxx, maxz +*/ +static int is_inside(int x, int z, int minx, int minz, int maxx, int maxz) { + return (x >= minx && x <= maxx && z >= minz && z <= maxz); +} + +static void get_color(void *data, + RenderState *state, + unsigned char *r, + unsigned char *g, + unsigned char *b, + unsigned char *a) { + /** + * Calculate the color at the current position and store the values to r,g,b,a. + **/ + RenderPrimitiveBounds *self = (RenderPrimitiveBounds *)data; + int x = (state->chunkx * 16) + state->x, z = (state->chunkz * 16) + state->z, col, cond; + struct Color *bounds = (struct Color *)(self->bounds); + struct Condition * c = NULL; + bool any = true; + + /** + * Check for every color in the current point is in the given bounds, + * and color appropriately + **/ + // iterate over all the colors + for ( col = 0; col < self->numcolors; col++) { + any = false; + // iterate over all conditions + for (cond = 0; cond < bounds[col].numconds; cond++) { + c = (struct Condition *)&bounds[col].conditions[cond]; + // check current point is in the condition + if(is_inside(x, z, c->minx, c->minz, c->maxx, c->maxz)) { + any = true; + } + } + + //if current point is in any of the conditions, draw it this color + if (any) { + // set the color + *r = bounds[col].r; + *g = bounds[col].g; + *b = bounds[col].b; + *a = bounds[col].a; + return; + } + } + return; +} + +static int overlay_bounds_start(void *data, RenderState *state, PyObject *support) { + /** + * Initializing the search for bounds by parsing the arguments and storing them into + * appropriate bounds. If no arguments are passed create and use default values. + **/ + PyObject *opt; + RenderPrimitiveBounds* self; + + /* first, chain up */ + int ret = primitive_overlay.start(data, state, support); + if (ret != 0) { + return ret; + } + + /* now do custom initializations */ + self = (RenderPrimitiveBounds *)data; + + // opt is a borrowed reference. do not deref + // store the bounds python object into opt. + if (!render_mode_parse_option(support, "bounds", "O", &(opt))) { + return 1; + } + + /** + * Check if a sane option was passed. + **/ + if (opt && opt != Py_None) { + struct Color *bounds = NULL; + struct Condition *cond = NULL; + Py_ssize_t bounds_size = 0, i, cond_size = 0, n = 0; + bool cont = true; + + opt = PySequence_Fast(opt, "expected a sequence"); + if (!opt) { + PyErr_SetString(PyExc_TypeError, "'bounds' must be a a sequence"); + return 1; + } + + bounds_size = PySequence_Fast_GET_SIZE(opt); + // Getting space on the heap and do not forget to set self->numcolors. + bounds = self->bounds = calloc(bounds_size, sizeof(struct Color)); + self->numcolors = bounds_size; + if (bounds == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + return 1; + } + + /** + * Try to parse the definitions of conditions and colors. + **/ + if (cont) { + for (i = 0; i < bounds_size; i++) { + PyObject *bound = PyList_GET_ITEM(opt, i); + // condspy holding the conditions tuple of variable length (python object) + PyObject *condspy; + // colorpy holding the 4 tuple with r g b a values of the color + PyObject *colorpy; + + // getting the condspy and colorpy out of the bounds. + if (!PyArg_ParseTuple(bound, "OO", &condspy, &colorpy)) { + // Exception set automatically + free(bounds); + self->bounds = NULL; + return 1; + } + + // Parse colorpy into a c-struct. + if (!PyArg_ParseTuple( colorpy, "bbbb", + &bounds[i].r, + &bounds[i].g, + &bounds[i].b, + &bounds[i].a)) { + free(bounds); + self->bounds = NULL; + return 1; + } + + // Convert condspy to a fast sequence + condspy = PySequence_Fast(condspy, "Failed to parse conditions"); + if(condspy == NULL) { + free(bounds); + self->bounds = NULL; + return 1; + } + + // get the number of conditions. + bounds[i].numconds = PySequence_Fast_GET_SIZE(condspy); + // reserve enough memory for the conditions. + cond = calloc(bounds[i].numconds, sizeof(struct Condition)); + bounds[i].conditions = cond; + + if (bounds[i].conditions == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + free(bounds); + self->bounds = NULL; + return 1; + } + + // iterate over all the conditions and read them. + for (n = 0; n < bounds[i].numconds; n++) { + PyObject *ccond = PySequence_Fast_GET_ITEM(condspy, n); + if(!PyArg_ParseTuple( ccond, "iiii", + &cond[n].minx, + &cond[n].minz, + &cond[n].maxx, + &cond[n].maxz)){ + int x = 0; + for(x = 0; x < bounds_size; x++){ + free(bounds[x].conditions); + } + free(bounds); + self->bounds = NULL; + return 1; + } + } + } + } + } + + /* setup custom color */ + self->parent.get_color = get_color; + + return 0; +} + +static void overlay_bounds_finish(void *data, RenderState *state) { + /* first free all *our* stuff */ + RenderPrimitiveBounds* self = (RenderPrimitiveBounds *)data; + int i = 0; + + if(self->bounds) { + // freeing the nested bounds + struct Color * m = self->bounds; + for(i = 0; i < self->numcolors; i++){ + if(m[i].conditions) { + free(m[i].conditions); + } + } + } + + if (self->bounds) { + free(self->bounds); + self->bounds = NULL; + } + + /* now, chain up */ + primitive_overlay.finish(data, state); +} + +RenderPrimitiveInterface primitive_overlay_bounds = { + "overlay-bounds", + sizeof(RenderPrimitiveBounds), + overlay_bounds_start, + overlay_bounds_finish, + NULL, + NULL, + overlay_draw, +}; +