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