Draw rectangles, circles or arbitrary polygons in a m x n matrix

前端 未结 5 1393
我寻月下人不归
我寻月下人不归 2021-02-14 00:19

I want to simulate the flow around objects in two dimensions. Therefore I wrote a program in C which uses the Navier-Stokes equations to describe the motion of fluids. Now I cam

相关标签:
5条回答
  • 2021-02-14 00:40

    If you want to be able to draw arbitrary shapes, you probably want to use SVG. I can recommend nanosvg.h and nanosvgrast.h with an example (also uses stb_image for other image formats and xcb for displaying the image in X11) It's also available as at github gist here

    #include <xcb/xcb.h>
    #include <xcb/xcb_image.h>
    #define STBI_NO_HDR
    #define STBI_NO_LINEAR
    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    #define NANOSVG_IMPLEMENTATION
    #include "nanosvg.h"
    #define NANOSVGRAST_IMPLEMENTATION
    #include "nanosvgrast.h"
    
    int main(int argc, char **argv){
       xcb_connection_t *c = xcb_connect(0, 0);
       xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
       int w, h, n,
          depth = s->root_depth,
          win_class = XCB_WINDOW_CLASS_INPUT_OUTPUT,
          format = XCB_IMAGE_FORMAT_Z_PIXMAP;
       xcb_colormap_t colormap = s->default_colormap;
       xcb_drawable_t win = xcb_generate_id(c);
       xcb_gcontext_t gc = xcb_generate_id(c);
       xcb_pixmap_t pixmap = xcb_generate_id(c);
       xcb_generic_event_t *ev;
       xcb_image_t *image;
       NSVGimage *shapes = NULL;
       NSVGrasterizer *rast = NULL;
       char *data = NULL;
       unsigned *dp;
       size_t i, len;
       uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
          value_mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS,
          values[] = { s->black_pixel, value_mask };
    
       if (argc<2) return -1;
       if ((data = stbi_load(argv[1], &w, &h, &n, 4)))
          ;
       else if ((shapes = nsvgParseFromFile(argv[1], "px", 96.0f))) {
          w = (int)shapes->width;
          h = (int)shapes->height;
          rast = nsvgCreateRasterizer();
          data = malloc(w*h*4);
          nsvgRasterize(rast, shapes, 0,0,1, data, w, h, w*4);
       }else return -1;
       for(i=0,len=w*h,dp=(unsigned *)data;i<len;i++) //rgba to bgra
          dp[i]=dp[i]&0xff00ff00|((dp[i]>>16)&0xFF)|((dp[i]<<16)&0xFF0000);
       xcb_create_window(c,depth,win,s->root,0,0,w,h,1,win_class,s->root_visual,mask,values);
       xcb_create_pixmap(c,depth,pixmap,win,w,h);
       xcb_create_gc(c,gc,pixmap,0,NULL);
       image = xcb_image_create_native(c,w,h,format,depth,data,w*h*4,data);
       xcb_image_put(c, pixmap, gc, image, 0, 0, 0);
       xcb_image_destroy(image);
       xcb_map_window(c, win);
       xcb_flush(c);
       while ((ev = xcb_wait_for_event(c))) {
          switch (ev->response_type & ~0x80){
          case XCB_EXPOSE: {
             xcb_expose_event_t *x = (xcb_expose_event_t *)ev;
             xcb_copy_area(c,pixmap,win,gc,x->x,x->y,x->x,x->y,x->width,x->height);
             xcb_flush(c);
          }break;
          case XCB_BUTTON_PRESS: goto end;
          default: break;
          }
       }
    end:
       xcb_free_pixmap(c, pixmap);
       xcb_disconnect(c);
       return 0;
    }
    

    You may need to modify the rasterizer code to fit to your specific format instead of X11, but you should be able to use any svg image editor to generate your shapes (or even just hand code them using a viewbox and path) For instance, draw your images in black and white and just use any one of the generated R,G or B bits in the RGBA result instead of converting it to the X11 pixel format.

    Using svg format will also allow you to convert it to any arbitrary image format (including those mentioned in the edit) while stretching it to any size making it easy to see how stretching the x or y dimensions affects flow. The svg format even allows a large number of transformations on individual​ shapes for fine tuning.

    0 讨论(0)
  • 2021-02-14 00:46

    I prefer to use 'sin' and 'cos' to make a circle. If you want some special shape like Oval. You can use the 'fac_specialX' and 'fac_specialY' make it different. If 'fac_specialX' and 'fac_specialY' not a fixed value(maybe be changed each time in loop), can make the shape more special.(or just try to modify a part of the circle array)

    int r=10;// radius
    int x0=25,y0=25; // center
    int circle_points = 300; // accuracy --> higher cause better quality but slow
    int circleX[circle_points]; // circle array
    int circleY[circle_points]; // circle array
    // #define PI 3.1415926
    double fac_angle =  ( 2*PI ) / circle_points;
    // normal circle : fac_specialX & fac_specialY  set 1
    // Oval : fac_specialX --> higher cause longer in X
    //          fac_specialY --> higher cause longer in Y
    double fac_specialX = 0.5;
    double fac_specialY = 1.5;
    // Calculate the coordinates
    for(int i=0 ; i<circle_points ; i++) {
    
        // #include <math.h>  ->> sin cos
        circleX[i] = x0 + r*sin( (i+1)*fac_angle )*fac_specialX;
        circleY[i] = y0 + r*cos( (i+1)*fac_angle )*fac_specialY;
        // set the ponts in M array
        M[ circleY[i] ][ circleX[i] ] = 1;
    }
    
    0 讨论(0)
  • 2021-02-14 00:51

    There might be more efficient ways of doing this, but here's one way.

    Define a function in C using the equation of the polygon you wish to draw. The function is defined such that it accepts a point coordinates, and returns whether the point lies inside the polygon or not. For example, for a circle, the function could accept the point (x,y), the centre (x0,y0), and the radius r, and return (x-x0)^2 + (y-y0)^2 - r^2 < 0. Let this function be f.

    Determine the bounding box rectangle of the polygon, if possible, or else, the smallest rectangle you can determine which completely encloses the polygon. This will give you a rectangular matrix.

    Now, iterate over the points in the rectangular matrix. For each point, call the function you previously defined. Assign the coordinate a 1 if it returns True, and 0 if it returns False. This will construct the polygon.

    Suppose you want to draw a circle with centre (x0,y0), radius r, then you can use:

    int f(int i, int j, int x0, int y0, int r)
    {
        return pow((i-x0),2) + pow((j-y0),2) - pow(r,2) < 0;        
    }
    
    
    for(int i = x0-r; i <= x0 + r; i++)
    {
        for(int j = y0-r; j <= y0 + r; j++)
        {
            if(f(i,j,x0,y0,r))
            {
                M[i][j] = 1;
            }
            else
            {
                M[i][j] = 0;
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-14 00:55

    The problem at hand boils down to rasterisation (Wikipedia); and to scan line conversion (siggraph.org) in particular.

    The siggraph.org article contains detailed explanations on how to draw straight lines, circles and ellipses, and convex and concave polygons.

    However, this is a problem that has already been solved a large number of times. While OP could certainly implement the necessary primitives (lines, ellipses, triangles, polygons), there is a much simpler approach.

    I suggest that OP implements a simple NetPBM format reader for P5 (binary grayscale pixmap) format, and the netpbm tools (from netpbm package in Linux distributions and BSD variants; see the Netpbm home page for other systems) to convert any image to an easy-to-read PGM (P5) file, where each pixel corresponds to one element in OP's matrix.

    That way, one can use e.g. Inkscape to draw the system using vector graphics, rasterize it at any size (by e.g. exporting as PNG image), convert to PGM (P5) format using the netpbm tools (pngtopnm or anytopnm, followed by ppmtopgm), and read the file. Indeed, in POSIX.1 systems (just about everywhere except in windows), one can use popen("anytopnm path-to-file | pnmtopng", "r") (or slightly more complicated two-fork() piped solution) to read any pixmap image in PGM (P5) format.

    Alternatively, one could consider using e.g. the ImageMagick library to read just about any format pixmap images (JPEG, GIF, PNG etc.).


    Personally, both as a developer and user (although note that I'm explicitly a non-Windows user; haven't used Microsoft products in over a decade), I would prefer the netpbm approach. The program, say mysim, would use e.g. /usr/lib/mysim/read-image shell script (or program in Windows, perhaps macs; or, if defined, the script or program defined by the MYSIM_READ_IMAGE environment variable), to read an image specified on the command line, emitting it in PGM (P5) format. The main program would simply read the output of the helper.

    This way, if a user needs special handling for input files, they can trivially copy the existing script, modify it to suit their own needs, and install somewhere under their own home directory (or globally, or even replace the existing one, if it is used by all users anyway).

    The program can use either popen() or fork()+execv() to execute the script, with the input filename as a command-line parameter, and read the output in the parent process to construct the initial matrix.

    I prefer this approach over the image library approach for a number of reasons. First, it is more modular, allowing the user to override the image reading mechanism and manipulate it if necessary. (In my experience, such overrides are not very often needed, but when they are, they are extremely useful, and definitely overall worth it.) Second, the image processing (which in many cases is quite complex) is done in a separate process, which means all memory (for code and data) needed to read and decipher the image are released when the image has been fully read. Third, this approach follows the Unix philosophy and the KISS principle, which have a proven track record of guiding the development of robust and useful tools.


    Here is an example program that reads a binary PBM, PGM, or PPM file (NetPBM P4, P5, and P6 formats, respectively) from the standard input, into a matrix structure, filling the matrix with 0 or 1 (based on the colors or grayscale values read from the image). For ease of testing, the program outputs the matrix to standard output in PGM (P5) format.

    The program follows the format specification in the NetPBM manual pages (for PBM (P4), PGM (P5), and PPM (P6) formats, respectively). The Wikipedia article on NetPBM formats currently show examples with invalid comments (between the header and the data). The NetPBM manual pages state that the final header value is followed by a single whitespace character, and not a comment. (If a comment may follow the final header value, it is impossible to know whether a # (binary 0x23 = 35) in the binary data starts a comment, or is an actual data value.)

    This is explicitly in the public domain, or, equivalently, licensed under the Creative Commons CC0 license. This means you are completely free to use the below code any way and anywhere you like, even in commercial projects, but that there are no guarantees: if it breaks, or breaks something, or sets your hair on fire, you get to keep all the pieces and only blame yourself.

    That said, it is only lightly tested, so if you find a bug in it, let me know in the comments so I can verify and fix.

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    /* Matrix to read data into */
    typedef struct {
        int            rows;
        int            cols;
        long           rowstride;
        long           colstride;
        unsigned char *data;        /* data[row*rowstride + col*colstride] */
    } matrix;
    #define MATRIX_INIT { 0, 0, 0, 0, NULL }
    
    /* NetPBM (binary) formats supported */
    #define PNM_PBM  4
    #define PNM_PGM  5
    #define PNM_PPM  6
    
    /* Error codes from pnm_*() functions */
    #define PNM_EOF       -1
    #define PNM_INVALID   -2
    #define PNM_OVERFLOW  -3
    
    
    /* This helper function returns the NetPBM file identifier;
       PNM_PBM, PNM_PGM, PNM_PPM, or PNM_INVALID if unsupported.
    */
    static int pnm_type(FILE *in)
    {
        /* First character must be 'P'. */
        if (getc(in) != 'P')
            return PNM_INVALID;
    
        /* Second character determines the type. */
        switch (getc(in)) {
        case '4': return PNM_PBM;
        case '5': return PNM_PGM;
        case '6': return PNM_PPM;
        default:  return PNM_INVALID;
        }
    }
    
    /* This helper function reads a number from a NetPBM header,
       correctly handling comments. Since all numbers in NetPBM
       headers are nonnegative, this function returns negative
       when an error occurs:
        -1: Premature end of input
        -2: Value is too large (int overflow)
        -3: Invalid input (not a NetPBM format file)
    */
    static int pnm_value(FILE *in)
    {
        int  c;
    
        /* Skip leading whitespace and comments. */
        c = getc(in);
        while (c == '\t' || c == '\n' || c == '\v' ||
               c == '\f' || c == '\r' || c == ' ' || c == '#')
            if (c == '#') {
                while (c != EOF && c != '\n')
                    c = getc(in);
            } else
                c = getc(in);
    
        if (c == EOF)
            return PNM_EOF;
    
        if (c >= '0' && c <= '9') {
            int value = 0;
    
            while (c >= '0' && c <= '9') {
                const int oldvalue = value;
                value = 10*value + (c - '0');
                if ((int)(value / 10) != oldvalue)
                    return PNM_OVERFLOW;
                c = getc(in);
            }
    
            /* Do not consume the separator. */
            if (c != EOF)
                ungetc(c, in);
    
            /* Success. */
            return value;
        }
    
        return PNM_INVALID;
    }
    
    /* This helper function consumes the single newline
       following the final value in the header.
       Returns 0 if success, PNM_INVALID otherwise.
    */
    static int pnm_newline(FILE *in)
    {
        int c;
    
        c = getc(in);
        if (c == '\r')
            c = getc(in);
        if (c == '\n')
            return 0;
    
        return PNM_INVALID;
    }
    
    static void pnm_matrix_free(matrix *to)
    {
        if (to) {
            free(to->data);
            to->rows = 0;
            to->cols = 0;
            to->rowstride = 0;
            to->colstride = 0;
            to->data = NULL;
        }
    }
    
    static int pnm_matrix_init(matrix *to, int rows, int cols)
    {
        size_t  cells, bytes;
    
        if (rows < 1 || cols < 1)
            return PNM_INVALID;
    
        cells = (size_t)rows * (size_t)cols;
        if ((size_t)(cells / (size_t)rows) != (size_t)cols ||
            (size_t)(cells / (size_t)cols) != (size_t)rows)
            return PNM_OVERFLOW;
    
        bytes = cells * sizeof to->data[0];
        if ((size_t)(bytes / sizeof to->data[0]) != cells)
            return PNM_OVERFLOW;
    
        to->data = malloc(bytes);
        if (!to->data)
            return PNM_OVERFLOW;
    
        to->rows = rows;
        to->cols = cols;
    
        /* Default to a row-major data order. */
        to->colstride = 1L;
        to->rowstride = cols;
    
        return 0;
    }
    
    static int pnm_p4_matrix(FILE *in, matrix *to)
    {
        int rows, cols, result, r, c, byte = 0;
    
        cols = pnm_value(in);
        if (cols < 1)
            return PNM_INVALID;
    
        rows = pnm_value(in);
        if (rows < 1)
            return PNM_INVALID;
    
        if (pnm_newline(in))
            return PNM_INVALID;
    
        result = pnm_matrix_init(to, rows, cols);
        if (result)
            return result;
    
        for (r = 0; r < rows; r++) {
            const long ri = r * to->rowstride;
            for (c = 0; c < cols; c++) {
                const long i = ri + c * to->colstride;
    
                switch (c & 7) {
                case 0:
                    byte = getc(in);
                    if (byte == EOF) {
                        pnm_matrix_free(to);
                        return PNM_INVALID;
                    }
                    to->data[i] = !!(byte & 128);
                    break;
                case 1:
                    to->data[i] = !!(byte & 64);
                    break;
                case 2:
                    to->data[i] = !!(byte & 32);
                    break;
                case 3:
                    to->data[i] = !!(byte & 16);
                    break;
                case 4:
                    to->data[i] = !!(byte & 8);
                    break;
                case 5:
                    to->data[i] = !!(byte & 4);
                    break;
                case 6:
                    to->data[i] = !!(byte & 2);
                    break;
                case 7:
                    to->data[i] = !!(byte & 1);
                    break;
                }
            }
        }
    
        return 0;
    }
    
    static int pnm_p5_matrix(FILE *in, matrix *to)
    {
        int rows, cols, max, r, c, result;
    
        cols = pnm_value(in);
        if (cols < 1)
            return PNM_INVALID;
    
        rows = pnm_value(in);
        if (rows < 1)
            return PNM_INVALID;
    
        max = pnm_value(in);
        if (max < 1 || max > 65535)
            return PNM_INVALID;
    
        if (pnm_newline(in))
            return PNM_INVALID;
    
        result = pnm_matrix_init(to, rows, cols);
        if (result)
            return result; 
    
        if (max < 256) {
            const int limit = (max + 1) / 2;
            int val;
            for (r = 0; r < rows; r++) {
                const long ri = r * to->rowstride;
                for (c = 0; c < cols; c++) {
                    const long i = ri + c * to->colstride;
    
                    val = getc(in);
                    if (val == EOF) {
                        pnm_matrix_free(to);
                        return PNM_INVALID;
                    }
    
                    to->data[i] = (val < limit);
                }
            }
        } else {
            const int limit = (max + 1) / 2;
            int val, low;
            for (r = 0; r < rows; r++) {
                const long ri = r * to->rowstride;
                for (c = 0; c < cols; c++) {
                    const long i = ri + c * to->colstride;
    
                    val = getc(in);
                    low = getc(in);
                    if (val == EOF || low == EOF) {
                        pnm_matrix_free(to);
                        return PNM_INVALID;
                    }
                    val = 256*val + low;
    
                    to->data[i] = (val < limit);
                }
            }
        }
    
        return 0;
    }
    
    static int pnm_p6_matrix(FILE *in, matrix *to)
    {
        int rows, cols, max, r, c, result;
    
        cols = pnm_value(in);
        if (cols < 1)
            return PNM_INVALID;
    
        rows = pnm_value(in);
        if (rows < 1)
            return PNM_INVALID;
    
        max = pnm_value(in);
        if (max < 1 || max > 65535)
            return PNM_INVALID;
    
        if (pnm_newline(in))
            return PNM_INVALID;
    
        result = pnm_matrix_init(to, rows, cols);
        if (result)
            return result;
    
        if (max < 256) {
            const int limit = 128 * max;
            int       val, rval, gval, bval;
    
            for (r = 0; r < rows; r++) {
                const long ri = r * to->rowstride;
                for (c = 0; c < cols; c++) {
                    const long i = ri + c * to->colstride;
    
                    rval = getc(in);
                    gval = getc(in);
                    bval = getc(in);
                    if (rval == EOF || gval == EOF || bval == EOF) {
                        pnm_matrix_free(to);
                        return PNM_INVALID;
                    }
    
                    val =  54 * rval
                        + 183 * gval
                        +  19 * bval;
    
                    to->data[i] = (val < limit);
                }
            }
        } else {
            const int limit = 128 * max;
            int       val, rhi, rlo, ghi, glo, bhi, blo;
    
            for (r = 0; r < rows; r++) {
                const long ri = r * to->rowstride;
                for (c = 0; c < cols; c++) {
                    const long i = ri + c * to->colstride;
    
                    rhi = getc(in);
                    rlo = getc(in);
                    ghi = getc(in);
                    glo = getc(in);
                    bhi = getc(in);
                    blo = getc(in);
                    if (rhi == EOF || rlo == EOF ||
                        ghi == EOF || glo == EOF ||
                        bhi == EOF || blo == EOF) {
                        pnm_matrix_free(to);
                        return PNM_INVALID;
                    }
    
                    val =  54 * (rhi*256 + rlo)
                        + 183 * (ghi*256 + glo)
                        +  19 * (bhi*256 + blo);
    
                    to->data[i] = (val < limit);
                }
            }
        }
    
        return 0;
    }
    
    int pnm_matrix(FILE *in, matrix *to)
    {
        /* If the matrix is specified, initialize it. */
        if (to) {
            to->rows = 0L;
            to->cols = 0L;
            to->rowstride = 0L;
            to->colstride = 0L;
            to->data = NULL;
        }
    
        /* Sanity checks on parameters. */
        if (!to || !in || ferror(in))
            return PNM_INVALID;
    
        switch (pnm_type(in)) {
        case PNM_PBM: return pnm_p4_matrix(in, to);
        case PNM_PGM: return pnm_p5_matrix(in, to);
        case PNM_PPM: return pnm_p6_matrix(in, to);
        default:      return PNM_INVALID;
        }
    }
    
    int main(void)
    {
        int r, c;
        matrix m = MATRIX_INIT;
    
        if (pnm_matrix(stdin, &m)) {
            fprintf(stderr, "Cannot parse standard input.\n");
            return EXIT_FAILURE;
        }
    
        fprintf(stderr, "Read %d rows, %d columns, from standard input.\n", m.rows, m.cols);
    
        /* For ease of debugging, we output the matrix as a PGM file. */
        printf("P5\n%d %d\n255\n", m.cols, m.rows);
        for (r = 0; r < m.rows; r++)
            for (c = 0; c < m.cols; c++)
                if (m.data[r * m.rowstride + c * m.colstride] == 0)
                    putchar(255); /* White */
                else
                    putchar(0);   /* Black */
    
        return EXIT_SUCCESS;
    }
    

    Note that I did not verify whether the bit/grayscale/color conversion is the right way with respect to how OP intends to use the matrix. (That is, whether "white" or light colours should yield a 0 or a 1 in the matrix.) If you need to invert it for PBM images, use !(byte & NUMBER) instead. If you need to invert it for PGM or PPM images, use (val >= limit) instead.

    The program should be valid C (even down to C89), and compile on any architecture. On silly architectures like Windows, you might have to open/reopen the standard input in "binary mode" (include b in the fopen() flags), as they otherwise may mangle the input.

    On Linux, I compiled and tested the program (example.c) with

    gcc -Wall -O2 example.c -o example
    ./example < inputfile.pbm > result-pbm.pgm
    ./example < inputfile.pgm > result-pgm.pgm
    ./example < inputfile.ppm > result-ppm.pgm
    
    0 讨论(0)
  • 2021-02-14 00:59

    If number of figures is not so high (e.g. less than 100) you can check for every pixel if it belongs to any polygon. You just need figure abstraction:

    /* Abstract struct for hloding figure (rectangle or elipse data)*/
    typedef struct _figure_t* figure_t;
    
    /* Pointer to pixel check algorithm implementation */
    typedef int (*is_pixel_belongs_t)(uint32_t, uint32_t, figure_t);
    
    struct _figure_t {
        is_pixel_belongs_t is_pixel_belongs;
    };
    
    /* figure implementation for rectangle */
    typedef struct _rectangle_t {
        is_pixel_belongs_t is_pixel_belongs;
        uint32_t x;
        uint32_t y;
        uint32_t width;
        uint32_t height;
    } * rectangle_t;
    
    int is_pixel_belongs_rectangle(uint32_t x, uint32_t y, rectangle_t rect) {
        int x_belongs (x >= rect->x) && (x <= (rect->x + rect->width));
        int y_belongs (y >= rect->y) && (y <= (rect->y + rect->height));
        return x_belongs && y_belongs;
    }
    
    figure_t make_rect(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
        rectangle_t result = (rectangle_t) malloc(sizeof(struct _rectangle_t));
        result->is_pixel_belongs = (is_pixel_belongs_t) is_pixel_belongs_rectangle;
        result->x = x;
        result->y = x;
        result->width  = width;
        result->height = height;
    }
    
    /* figure implementation for elipse */
    typedef struct _rectangle_t {
        is_pixel_belongs_t is_pixel_belongs;
        uint32_t x;
        uint32_t y;
        uint32_t width;
        uint32_t height;
    } * rectangle_t;
    
    /* Elipse implementation */
    /* x^2/a^2 + y^2/b^2 = 1*/
    figure_t make_elipse(uint32_t x, uint32_t y, uint32_t a, uint32_t b);
    
    
    void main() {
        #define NUM_FIGURES 10
        figure_t figures[NUM_FIGURES] = {
            make_rect(0, 0, 40, 40),
            make_elipse(256, 128, 80, 40),
            /* Add more figures*/
        }
    
        /* Initialize your image */
    
        /* For each pixel */
        for(uint32_t x = 0; x < width; ++x) {
            for(uint32_t y = 0; y < height; ++x) {
                /* For each figure check if pixel (x,y) belongs to it*/
                for(uint32_t figure_ii = 0; figure_ii < NUM_FIGURES; ++figure_ii) {
                    if (figures[figure_ii]->is_pixel_belongs(x, y)) {
                        image[x][y] = 1;
                        break;
                    }
                }
            }
        }
    }
    

    This is pretty straightforward approach and close to what you did. Inner loop by figures may affect performance, if you need to draw thousands/millions of arbitrary figures you would need to use aux structures. One option is binary space partitioning approach. I.e. organize your figures to binary tree so you can find a figure by pixel in O(log(n)) time where n is number of figures. Or you can spit your image to uniform grid and keep figures list for each tile.

    0 讨论(0)
提交回复
热议问题