Home | History | Annotate | Download | only in src
      1 /*
      2 ** 2010 August 28
      3 **
      4 ** The author disclaims copyright to this source code.  In place of
      5 ** a legal notice, here is a blessing:
      6 **
      7 **    May you do good and not evil.
      8 **    May you find forgiveness for yourself and forgive others.
      9 **    May you share freely, never taking more than you give.
     10 **
     11 *************************************************************************
     12 ** Code for testing all sorts of SQLite interfaces. This code
     13 ** is not included in the SQLite library.
     14 */
     15 
     16 #include <sqlite3.h>
     17 
     18 /* Solely for the UNUSED_PARAMETER() macro. */
     19 #include "sqliteInt.h"
     20 
     21 /*
     22 ** Type used to cache parameter information for the "circle" r-tree geometry
     23 ** callback.
     24 */
     25 typedef struct Circle Circle;
     26 struct Circle {
     27   struct Box {
     28     double xmin;
     29     double xmax;
     30     double ymin;
     31     double ymax;
     32   } aBox[2];
     33   double centerx;
     34   double centery;
     35   double radius;
     36 };
     37 
     38 /*
     39 ** Destructor function for Circle objects allocated by circle_geom().
     40 */
     41 static void circle_del(void *p){
     42   sqlite3_free(p);
     43 }
     44 
     45 /*
     46 ** Implementation of "circle" r-tree geometry callback.
     47 */
     48 static int circle_geom(
     49   sqlite3_rtree_geometry *p,
     50   int nCoord,
     51   double *aCoord,
     52   int *pRes
     53 ){
     54   int i;                          /* Iterator variable */
     55   Circle *pCircle;                /* Structure defining circular region */
     56   double xmin, xmax;              /* X dimensions of box being tested */
     57   double ymin, ymax;              /* X dimensions of box being tested */
     58 
     59   if( p->pUser==0 ){
     60     /* If pUser is still 0, then the parameter values have not been tested
     61     ** for correctness or stored into a Circle structure yet. Do this now. */
     62 
     63     /* This geometry callback is for use with a 2-dimensional r-tree table.
     64     ** Return an error if the table does not have exactly 2 dimensions. */
     65     if( nCoord!=4 ) return SQLITE_ERROR;
     66 
     67     /* Test that the correct number of parameters (3) have been supplied,
     68     ** and that the parameters are in range (that the radius of the circle
     69     ** radius is greater than zero). */
     70     if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
     71 
     72     /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
     73     ** if the allocation fails. */
     74     pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
     75     if( !pCircle ) return SQLITE_NOMEM;
     76     p->xDelUser = circle_del;
     77 
     78     /* Record the center and radius of the circular region. One way that
     79     ** tested bounding boxes that intersect the circular region are detected
     80     ** is by testing if each corner of the bounding box lies within radius
     81     ** units of the center of the circle. */
     82     pCircle->centerx = p->aParam[0];
     83     pCircle->centery = p->aParam[1];
     84     pCircle->radius = p->aParam[2];
     85 
     86     /* Define two bounding box regions. The first, aBox[0], extends to
     87     ** infinity in the X dimension. It covers the same range of the Y dimension
     88     ** as the circular region. The second, aBox[1], extends to infinity in
     89     ** the Y dimension and is constrained to the range of the circle in the
     90     ** X dimension.
     91     **
     92     ** Then imagine each box is split in half along its short axis by a line
     93     ** that intersects the center of the circular region. A bounding box
     94     ** being tested can be said to intersect the circular region if it contains
     95     ** points from each half of either of the two infinite bounding boxes.
     96     */
     97     pCircle->aBox[0].xmin = pCircle->centerx;
     98     pCircle->aBox[0].xmax = pCircle->centerx;
     99     pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
    100     pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
    101     pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
    102     pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
    103     pCircle->aBox[1].ymin = pCircle->centery;
    104     pCircle->aBox[1].ymax = pCircle->centery;
    105   }
    106 
    107   pCircle = (Circle *)p->pUser;
    108   xmin = aCoord[0];
    109   xmax = aCoord[1];
    110   ymin = aCoord[2];
    111   ymax = aCoord[3];
    112 
    113   /* Check if any of the 4 corners of the bounding-box being tested lie
    114   ** inside the circular region. If they do, then the bounding-box does
    115   ** intersect the region of interest. Set the output variable to true and
    116   ** return SQLITE_OK in this case. */
    117   for(i=0; i<4; i++){
    118     double x = (i&0x01) ? xmax : xmin;
    119     double y = (i&0x02) ? ymax : ymin;
    120     double d2;
    121 
    122     d2  = (x-pCircle->centerx)*(x-pCircle->centerx);
    123     d2 += (y-pCircle->centery)*(y-pCircle->centery);
    124     if( d2<(pCircle->radius*pCircle->radius) ){
    125       *pRes = 1;
    126       return SQLITE_OK;
    127     }
    128   }
    129 
    130   /* Check if the bounding box covers any other part of the circular region.
    131   ** See comments above for a description of how this test works. If it does
    132   ** cover part of the circular region, set the output variable to true
    133   ** and return SQLITE_OK. */
    134   for(i=0; i<2; i++){
    135     if( xmin<=pCircle->aBox[i].xmin
    136      && xmax>=pCircle->aBox[i].xmax
    137      && ymin<=pCircle->aBox[i].ymin
    138      && ymax>=pCircle->aBox[i].ymax
    139     ){
    140       *pRes = 1;
    141       return SQLITE_OK;
    142     }
    143   }
    144 
    145   /* The specified bounding box does not intersect the circular region. Set
    146   ** the output variable to zero and return SQLITE_OK. */
    147   *pRes = 0;
    148   return SQLITE_OK;
    149 }
    150 
    151 /* END of implementation of "circle" geometry callback.
    152 **************************************************************************
    153 *************************************************************************/
    154 
    155 #include <assert.h>
    156 #include "tcl.h"
    157 
    158 typedef struct Cube Cube;
    159 struct Cube {
    160   double x;
    161   double y;
    162   double z;
    163   double width;
    164   double height;
    165   double depth;
    166 };
    167 
    168 static void cube_context_free(void *p){
    169   sqlite3_free(p);
    170 }
    171 
    172 /*
    173 ** The context pointer registered along with the 'cube' callback is
    174 ** always ((void *)&gHere). This is just to facilitate testing, it is not
    175 ** actually used for anything.
    176 */
    177 static int gHere = 42;
    178 
    179 /*
    180 ** Implementation of a simple r-tree geom callback to test for intersection
    181 ** of r-tree rows with a "cube" shape. Cubes are defined by six scalar
    182 ** coordinates as follows:
    183 **
    184 **   cube(x, y, z, width, height, depth)
    185 **
    186 ** The width, height and depth parameters must all be greater than zero.
    187 */
    188 static int cube_geom(
    189   sqlite3_rtree_geometry *p,
    190   int nCoord,
    191   double *aCoord,
    192   int *piRes
    193 ){
    194   Cube *pCube = (Cube *)p->pUser;
    195 
    196   assert( p->pContext==(void *)&gHere );
    197 
    198   if( pCube==0 ){
    199     if( p->nParam!=6 || nCoord!=6
    200      || p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0
    201     ){
    202       return SQLITE_ERROR;
    203     }
    204     pCube = (Cube *)sqlite3_malloc(sizeof(Cube));
    205     if( !pCube ){
    206       return SQLITE_NOMEM;
    207     }
    208     pCube->x = p->aParam[0];
    209     pCube->y = p->aParam[1];
    210     pCube->z = p->aParam[2];
    211     pCube->width = p->aParam[3];
    212     pCube->height = p->aParam[4];
    213     pCube->depth = p->aParam[5];
    214 
    215     p->pUser = (void *)pCube;
    216     p->xDelUser = cube_context_free;
    217   }
    218 
    219   assert( nCoord==6 );
    220   *piRes = 0;
    221   if( aCoord[0]<=(pCube->x+pCube->width)
    222    && aCoord[1]>=pCube->x
    223    && aCoord[2]<=(pCube->y+pCube->height)
    224    && aCoord[3]>=pCube->y
    225    && aCoord[4]<=(pCube->z+pCube->depth)
    226    && aCoord[5]>=pCube->z
    227   ){
    228     *piRes = 1;
    229   }
    230 
    231   return SQLITE_OK;
    232 }
    233 
    234 static int register_cube_geom(
    235   void * clientData,
    236   Tcl_Interp *interp,
    237   int objc,
    238   Tcl_Obj *CONST objv[]
    239 ){
    240 #ifndef SQLITE_ENABLE_RTREE
    241   UNUSED_PARAMETER(clientData);
    242   UNUSED_PARAMETER(interp);
    243   UNUSED_PARAMETER(objc);
    244   UNUSED_PARAMETER(objv);
    245 #else
    246   extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
    247   extern const char *sqlite3TestErrorName(int);
    248   sqlite3 *db;
    249   int rc;
    250 
    251   if( objc!=2 ){
    252     Tcl_WrongNumArgs(interp, 1, objv, "DB");
    253     return TCL_ERROR;
    254   }
    255   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
    256   rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere);
    257   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
    258 #endif
    259   return TCL_OK;
    260 }
    261 
    262 static int register_circle_geom(
    263   void * clientData,
    264   Tcl_Interp *interp,
    265   int objc,
    266   Tcl_Obj *CONST objv[]
    267 ){
    268 #ifndef SQLITE_ENABLE_RTREE
    269   UNUSED_PARAMETER(clientData);
    270   UNUSED_PARAMETER(interp);
    271   UNUSED_PARAMETER(objc);
    272   UNUSED_PARAMETER(objv);
    273 #else
    274   extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**);
    275   extern const char *sqlite3TestErrorName(int);
    276   sqlite3 *db;
    277   int rc;
    278 
    279   if( objc!=2 ){
    280     Tcl_WrongNumArgs(interp, 1, objv, "DB");
    281     return TCL_ERROR;
    282   }
    283   if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
    284   rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
    285   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
    286 #endif
    287   return TCL_OK;
    288 }
    289 
    290 int Sqlitetestrtree_Init(Tcl_Interp *interp){
    291   Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0);
    292   Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0);
    293   return TCL_OK;
    294 }
    295