test_svx_bintree_trace_ray.c (17791B)
1 /* Copyright (C) 2018, 2020-2025 |Méso|Star> (contact@meso-star.com) 2 * Copyright (C) 2018 Université Paul Sabatier 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.r.org/licenses/>. */ 16 17 #include "svx.h" 18 #include "test_svx_utils.h" 19 20 #include <rsys/image.h> 21 #include <rsys/double2.h> 22 #include <rsys/double3.h> 23 #include <rsys/math.h> 24 25 #include <math.h> 26 27 struct scene { 28 enum svx_axis axis; 29 double origin; 30 double vxsz; 31 double range_center; 32 double range_half_len; 33 }; 34 35 static double 36 rand_canonic(void) 37 { 38 return (double)rand() / (double)((size_t)RAND_MAX + 1); 39 } 40 41 static void 42 voxel_get(const size_t xyz[3], const uint64_t mcode, void* dst, void* ctx) 43 { 44 const struct scene* scn = ctx; 45 char* val = dst; 46 double low; 47 double upp; 48 double dst1, dst2; 49 double min_dst, max_dst; 50 CHK(xyz != NULL); 51 CHK(dst != NULL); 52 CHK(ctx != NULL); 53 CHK(mcode == xyz[scn->axis]); 54 55 /* Compute the range of the voxel */ 56 low = (double)xyz[scn->axis] * scn->vxsz + scn->origin; 57 upp = low + scn->vxsz; 58 59 /* Compute the distance from the voxel boundary to the range_center */ 60 dst1 = fabs(scn->range_center - low); 61 dst2 = fabs(scn->range_center - upp); 62 min_dst = MMIN(dst1, dst2); 63 max_dst = MMAX(dst1, dst2); 64 65 /* Define if the voxel intersects a range boundary */ 66 *val = min_dst <= scn->range_half_len 67 && max_dst >= scn->range_half_len; 68 } 69 70 static void 71 voxels_merge(void* dst, const void* voxels[], const size_t nvoxels, void* ctx) 72 { 73 size_t ivoxel; 74 char tmp = 0; 75 char* val = dst; 76 77 CHK(dst && voxels && nvoxels && ctx); 78 79 for(ivoxel=0; !tmp && ivoxel<nvoxels; ++ivoxel) { 80 const char* voxel_data = voxels[ivoxel]; 81 tmp = *voxel_data; 82 } 83 *val = tmp; 84 } 85 86 static int 87 voxels_challenge_merge 88 (const struct svx_voxel voxels[], const size_t nvoxels, void* ctx) 89 { 90 size_t ivoxel; 91 int merge = 1; 92 char ref; 93 94 CHK(voxels && nvoxels && ctx); 95 96 ref = *(char*)(voxels[0].data); 97 98 for(ivoxel=1; merge && ivoxel<nvoxels; ++ivoxel) { 99 const char* voxel_data = voxels[ivoxel].data; 100 merge = (ref == *voxel_data); 101 } 102 return merge; 103 } 104 105 static int 106 hit_challenge 107 (const struct svx_hit* hit, 108 const double ray_org[3], 109 const double ray_dir[3], 110 const double ray_range[2], 111 void* context) 112 { 113 (void)context; 114 CHK(hit && ray_org && ray_dir && ray_range); 115 CHK(!SVX_HIT_NONE(hit)); 116 return 1; 117 } 118 119 static int 120 hit_challenge_root 121 (const struct svx_hit* hit, 122 const double ray_org[3], 123 const double ray_dir[3], 124 const double ray_range[2], 125 void* context) 126 { 127 (void)hit, (void)ray_org, (void)ray_dir, (void)ray_range, (void)context; 128 return hit->voxel.depth == 0; 129 } 130 131 static int 132 hit_challenge_pass_through 133 (const struct svx_hit* hit, 134 const double ray_org[3], 135 const double ray_dir[3], 136 const double ray_range[2], 137 void* context) 138 { 139 const struct ray* ray = context; 140 CHK(hit && ray_org && ray_dir && ray_range && context); 141 CHK(!SVX_HIT_NONE(hit)); 142 CHK(d3_eq(ray->org, ray_org)); 143 CHK(d2_eq(ray->range, ray_range)); 144 145 /* The direction could be adjusted internally when crossing a binary tree. 146 * This avoids numerical inaccuracies in the case of distant intersections, 147 * i.e. when the ray direction is roughly aligned with the third dimension 148 * (i.e. the one that goes to infinity). In this case, the ray direction is 149 * forced to be 0 on this dimension before being renormalized. The ray 150 * therefore never intersects the binary tree. But its direction has changed. 151 * Hence the approximate equality on the direction */ 152 CHK(d3_eq_eps(ray->dir, ray_dir, 1.e-6)); 153 154 return 0; 155 } 156 157 static int 158 hit_filter 159 (const struct svx_hit* hit, 160 const double ray_org[3], 161 const double ray_dir[3], 162 const double ray_range[3], 163 void* context) 164 { 165 const struct ray* ray = context; 166 CHK(hit && ray_org && ray_dir && ray_range && context); 167 CHK(d3_eq(ray->org, ray_org)); 168 CHK(d2_eq(ray->range, ray_range)); 169 CHK(!SVX_HIT_NONE(hit)); 170 171 /* The direction could be adjusted internally when crossing a binary tree. 172 * Refer to the hit_challenge_pass_through function */ 173 CHK(d3_eq_eps(ray->dir, ray_dir, 1.e-6)); 174 175 return *((char*)hit->voxel.data) == 0; 176 } 177 178 static int 179 hit_filter2 180 (const struct svx_hit* hit, 181 const double ray_org[3], 182 const double ray_dir[3], 183 const double ray_range[3], 184 void* context) 185 { 186 const struct svx_voxel* voxel = context; 187 CHK(hit && ray_org && ray_dir && ray_range && context); 188 CHK(!SVX_HIT_NONE(hit)); 189 return SVX_VOXEL_EQ(&hit->voxel, voxel) 190 || *((char*)hit->voxel.data) == 0; 191 } 192 193 static int 194 hit_filter3 195 (const struct svx_hit* hit, 196 const double ray_org[3], 197 const double ray_dir[3], 198 const double ray_range[3], 199 void* context) 200 { 201 int* accum = context; 202 CHK(hit && ray_org && ray_dir && ray_range && context); 203 CHK(!SVX_HIT_NONE(hit)); 204 *accum += *(char*)hit->voxel.data; 205 return 1; 206 } 207 208 static void 209 draw_image(struct image* img, struct svx_tree* btree) 210 { 211 struct camera cam; 212 unsigned char* pixels = NULL; 213 const size_t width = 512; 214 const size_t height = 512; 215 const double pos[3] = { 0, 0, 0}; 216 const double tgt[3] = { 0, 0, 1}; 217 const double up[3] = { 1, 0, 0}; 218 double pix[2]; 219 size_t ix, iy; 220 221 CHK(btree && img); 222 camera_init(&cam, pos, tgt, up, (double)width/(double)height); 223 224 CHK(image_setup(img, width, height, sizeof_image_format(IMAGE_RGB8)*width, 225 IMAGE_RGB8, NULL) == RES_OK); 226 pixels = (unsigned char*)img->pixels; 227 228 FOR_EACH(iy, 0, height) { 229 pix[1] = (double)iy / (double)height; 230 FOR_EACH(ix, 0, width) { 231 struct svx_hit hit; 232 struct ray r; 233 size_t ipix = (iy*width + ix)*3/*#channels per pixel*/; 234 pix[0] = (double)ix / (double)width; 235 camera_ray(&cam, pix, &r); 236 237 CHK(svx_tree_trace_ray(btree, r.org, r.dir, r.range, 238 hit_challenge_pass_through, hit_filter, &r, &hit) == RES_OK); 239 pixels[ipix+0] = 0; 240 pixels[ipix+1] = 0; 241 pixels[ipix+2] = 0; 242 243 if(!SVX_HIT_NONE(&hit)) { 244 double N[3] = {1, 0, 0}; 245 const double dot = d3_dot(N, r.dir); 246 if(dot < 0) { 247 pixels[ipix+0] =(unsigned char)(255 * -dot); 248 } else { 249 pixels[ipix+2] =(unsigned char)(255 * dot); 250 } 251 } 252 } 253 } 254 } 255 256 int 257 main(int argc, char** argv) 258 { 259 struct image img, img2; 260 FILE* stream = NULL; 261 struct svx_device* dev = NULL; 262 struct svx_tree* btree = NULL; 263 struct svx_tree* btree2 = NULL; 264 struct svx_voxel_desc voxel_desc = SVX_VOXEL_DESC_NULL; 265 struct svx_voxel voxel = SVX_VOXEL_NULL; 266 struct svx_hit hit; 267 struct svx_hit hit2; 268 struct scene scn; 269 const double lower = -1; 270 const double upper = 1; 271 const size_t definition = 32; 272 const double scnsz = upper - lower; 273 struct ray r; 274 int accum; 275 (void)argc, (void)argv; 276 277 scn.origin = lower; 278 scn.vxsz = scnsz / (double)definition; 279 scn.range_center = lower + scnsz*0.5; 280 scn.range_half_len = scnsz*0.25; 281 scn.axis = SVX_AXIS_X; 282 283 CHK(svx_device_create(NULL, NULL, 1, &dev) == RES_OK); 284 voxel_desc.get = voxel_get; 285 voxel_desc.merge = voxels_merge; 286 voxel_desc.challenge_merge = voxels_challenge_merge; 287 voxel_desc.context = &scn; 288 voxel_desc.size = sizeof(char); 289 290 CHK(svx_bintree_create(dev, lower, upper, definition, scn.axis, 291 &voxel_desc, &btree) == RES_OK); 292 293 /* Duplicate the binary tree through serialization */ 294 CHK(stream = tmpfile()); 295 CHK(svx_tree_write(btree, stream) == RES_OK); 296 CHK(svx_tree_create_from_stream(dev, stream, &btree2) == RES_BAD_ARG); 297 rewind(stream); 298 CHK(svx_tree_create_from_stream(dev, stream, &btree2) == RES_OK); 299 fclose(stream); 300 301 #define RT svx_tree_trace_ray 302 d3(r.org, -1.01, 0, 0); 303 d3(r.dir, -1, 0, 0); 304 d2(r.range, 0, DBL_MAX); 305 306 CHK(RT(NULL, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 307 CHK(RT(btree, NULL, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 308 CHK(RT(btree, r.org, NULL, r.range, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 309 CHK(RT(btree, r.org, r.dir, NULL, NULL, NULL, NULL, &hit) == RES_BAD_ARG); 310 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, NULL) == RES_BAD_ARG); 311 312 /* Check failed hits */ 313 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 314 CHK(SVX_HIT_NONE(&hit)); 315 d3(r.org, 1.01, 0, 0); 316 d3(r.dir, 1, 0, 0); 317 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 318 CHK(SVX_HIT_NONE(&hit)); 319 d3(r.dir, 0, 1, 0); 320 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 321 CHK(SVX_HIT_NONE(&hit)); 322 d3(r.dir, 0, 0, -1); 323 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 324 CHK(SVX_HIT_NONE(&hit)); 325 326 /* Check first hit */ 327 d3(r.org, -1.01, 0, 0); 328 d3(r.dir, 1, 0, 0); 329 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 330 CHK(!SVX_HIT_NONE(&hit)); 331 CHK(eq_eps(hit.distance[0], 0.01, 1.e-6)); 332 CHK(eq_eps(hit.distance[0], hit.voxel.lower[0] - r.org[0], 1.e-6)); 333 CHK(eq_eps(hit.distance[1], hit.voxel.upper[0] - r.org[0], 1.e-6)); 334 CHK(hit.voxel.is_leaf); 335 CHK(hit.voxel.depth == 3); 336 CHK(*(char*)hit.voxel.data == 0); 337 CHK(IS_INF(hit.voxel.lower[1])); 338 CHK(IS_INF(hit.voxel.lower[2])); 339 CHK(IS_INF(hit.voxel.upper[1])); 340 CHK(IS_INF(hit.voxel.upper[2])); 341 342 /* Check first hit with negative ray */ 343 d3(r.org, 1.01, 1234, 10); 344 d3(r.dir, -1, 0, 0); 345 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit2) == RES_OK); 346 CHK(!SVX_HIT_NONE(&hit2)); 347 CHK(eq_eps(hit2.distance[0], 0.01, 1.e-6)); 348 CHK(eq_eps(hit2.distance[0], r.org[0] - hit2.voxel.upper[0], 1.e-6)); 349 CHK(eq_eps(hit2.distance[1], r.org[0] - hit2.voxel.lower[0], 1.e-6)); 350 CHK(hit2.voxel.is_leaf); 351 CHK(*(char*)hit2.voxel.data == 0); 352 CHK(eq_eps(hit2.distance[0], hit2.distance[0], 1.e-6)); 353 CHK(eq_eps(hit2.distance[1], hit2.distance[1], 1.e-6)); 354 355 /* Check first hit with negative ray and and a non orthognal r.dir */ 356 d3_normalize(r.dir, d3(r.dir, -1, -1, -1)); 357 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 358 CHK(!SVX_HIT_NONE(&hit)); 359 CHK(SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); 360 CHK(eq_eps(hit.distance[0], (hit.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-4)); 361 CHK(eq_eps(hit.distance[1], (hit.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-4)); 362 CHK(eq_eps(hit.voxel.upper[0], 1, 1.e-6)); 363 CHK(eq_eps(hit.voxel.lower[0], 1-2*(1/(double)(1<<hit.voxel.depth)), 1.e-6)); 364 CHK(hit.voxel.is_leaf); 365 CHK(hit.voxel.depth == 3); 366 CHK(*(char*)hit.voxel.data == 0); 367 368 /* Use challenge functor to intersect voxels that are note leaves */ 369 d3(r.org, 2, 1, 0); 370 d3(r.dir, -rand_canonic(), rand_canonic()*2-1, rand_canonic()*2-1); 371 d3_normalize(r.dir, r.dir); 372 CHK(RT(btree, r.org, r.dir, r.range, hit_challenge, NULL, NULL, &hit)==RES_OK); 373 CHK(!SVX_HIT_NONE(&hit)); 374 CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); 375 CHK(hit.voxel.is_leaf == 0); 376 CHK(hit.voxel.depth < 3); 377 CHK(*(char*)hit.voxel.data == 1); 378 CHK(eq_eps(hit.distance[0], (hit.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-6)); 379 CHK(eq_eps(hit.distance[1], (hit.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-6)); 380 381 /* Use filter function to discard leaves with a value == 0 */ 382 CHK(RT(btree, r.org, r.dir, r.range, hit_challenge_pass_through, hit_filter, 383 &r, &hit) == RES_OK); 384 CHK(!SVX_HIT_NONE(&hit)); 385 CHK(hit.voxel.is_leaf == 1); 386 CHK(hit.voxel.depth == 5); 387 CHK(*(char*)hit.voxel.data == 1); 388 CHK(eq_eps(hit.distance[0], (hit.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-4)); 389 CHK(eq_eps(hit.distance[1], (hit.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-4)); 390 CHK(eq_eps(hit.voxel.lower[0], 0.5, 1.e-6)); 391 CHK(eq_eps(hit.voxel.upper[0], 0.5+2.0/32.0, 1.e-6)); 392 393 /* Use the ray range to avoid the intersection with the closest voxel */ 394 r.range[1] = hit.distance[0] - 1.e-6; 395 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); 396 CHK(SVX_HIT_NONE(&hit)); 397 r.range[1] = INF; 398 399 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); 400 CHK(!SVX_HIT_NONE(&hit)); 401 402 /* Use the ray range to discard the closest voxel */ 403 r.range[0] = hit.distance[1] + 1.e-6; 404 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit2) == RES_OK); 405 CHK(!SVX_HIT_NONE(&hit2)); 406 CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); 407 CHK(hit2.voxel.is_leaf == 1); 408 CHK(hit2.voxel.depth == 5); 409 CHK(*(char*)hit2.voxel.data == 1); 410 CHK(eq_eps(hit2.distance[0], (hit2.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-4)); 411 CHK(eq_eps(hit2.distance[1], (hit2.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-4)); 412 CHK(eq_eps(hit2.voxel.lower[0], 0.5-2.0/32.0, 1.e-6)); 413 CHK(eq_eps(hit2.voxel.upper[0], 0.5, 1.e-6)); 414 415 /* Adjust the ray range to intersect the interior of the closest voxel */ 416 r.range[0] = hit.distance[0] + 1.e-6; 417 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit2) == RES_OK); 418 CHK(!SVX_HIT_NONE(&hit2)); 419 CHK(SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); 420 CHK(eq_eps(hit2.distance[0], r.range[0], 1.e-6)); 421 CHK(eq_eps(hit2.distance[1], hit.distance[1], 1.e-6)); 422 423 /* Discard the closest voxel != 0 with the filter function */ 424 d2(r.range, 0, INF); 425 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter2, &hit.voxel, &hit2) == RES_OK); 426 CHK(!SVX_HIT_NONE(&hit2)); 427 CHK(!SVX_VOXEL_EQ(&hit.voxel, &hit2.voxel)); 428 CHK(eq_eps(hit2.distance[0], (hit2.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-4)); 429 CHK(eq_eps(hit2.distance[1], (hit2.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-4)); 430 CHK(eq_eps(hit2.voxel.lower[0], 0.5-2.0/32.0, 1.e-6)); 431 CHK(eq_eps(hit2.voxel.upper[0], 0.5, 1.e-6)); 432 433 /* Use the filter functor to accumulate the leaves */ 434 accum = 0; 435 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter3, &accum, &hit) == RES_OK); 436 CHK(SVX_HIT_NONE(&hit)); 437 CHK(accum == 4); 438 439 /* Check ray with null dir along the btree axis */ 440 d2(r.range, 0, INF); 441 d3(r.org, -0.875, 123, 456); 442 d3(r.dir, 0, rand_canonic()*2-1, rand_canonic()*2-1); 443 d3_normalize(r.dir, r.dir); 444 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 445 CHK(!SVX_HIT_NONE(&hit)); 446 CHK(eq_eps(hit.distance[0], r.range[0], 1.e-6)); 447 CHK(IS_INF(hit.distance[1])); 448 CHK(hit.voxel.is_leaf == 1); 449 CHK(hit.voxel.depth == 3); 450 CHK(svx_tree_at(btree, r.org, NULL, NULL, &voxel) == RES_OK); 451 CHK(SVX_VOXEL_EQ(&voxel, &hit.voxel)); 452 CHK(*(char*)hit.voxel.data == 0); 453 454 /* Use hit filter to discard the hit */ 455 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter, &r, &hit) == RES_OK); 456 CHK(SVX_HIT_NONE(&hit)); 457 458 /* Check ray with null dir along the btree axis */ 459 d3(r.org, -0.625, 456, 789); 460 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 461 CHK(!SVX_HIT_NONE(&hit)); 462 CHK(eq_eps(hit.distance[0], r.range[0], 1.e-6)); 463 CHK(IS_INF(hit.distance[1])); 464 CHK(hit.voxel.is_leaf == 1); 465 CHK(svx_tree_at(btree, r.org, NULL, NULL, &voxel) == RES_OK); 466 CHK(SVX_VOXEL_EQ(&voxel, &hit.voxel)); 467 CHK(*(char*)hit.voxel.data == 0); 468 469 /* Use hit chalenge to intersect a non leaf node */ 470 CHK(RT(btree, r.org, r.dir, r.range, hit_challenge, NULL, NULL, &hit2) == RES_OK); 471 CHK(eq_eps(hit.distance[0], r.range[0], 1.e-6)); 472 CHK(IS_INF(hit.distance[1])); 473 CHK(!SVX_HIT_NONE(&hit2)); 474 CHK(!SVX_VOXEL_EQ(&hit2.voxel, &hit.voxel)); 475 CHK(*(char*)hit2.voxel.data == 1); 476 CHK(hit2.voxel.depth < 3); 477 478 /* Still check a ray with null dir along the tree axis */ 479 d3(r.org, -0.51, 31, 41); 480 CHK(RT(btree, r.org, r.dir, r.range, NULL, NULL, NULL, &hit) == RES_OK); 481 CHK(eq_eps(hit.distance[0], r.range[0], 1.e-6)); 482 CHK(IS_INF(hit.distance[1])); 483 CHK(hit.voxel.is_leaf == 1); 484 CHK(svx_tree_at(btree, r.org, NULL, NULL, &voxel) == RES_OK); 485 CHK(SVX_VOXEL_EQ(&voxel, &hit.voxel)); 486 CHK(*(char*)hit.voxel.data == 1); 487 CHK(eq_eps(hit.voxel.lower[0], -0.5-2.0/32.0, 1.e-6)); 488 CHK(eq_eps(hit.voxel.upper[0], -0.5, 1.e-6)); 489 CHK(IS_INF(hit.voxel.lower[1])); 490 CHK(IS_INF(hit.voxel.lower[2])); 491 CHK(IS_INF(hit.voxel.upper[1])); 492 CHK(IS_INF(hit.voxel.upper[2])); 493 494 /* Use hit filter to accum data along the ray */ 495 accum = 0; 496 CHK(RT(btree, r.org, r.dir, r.range, NULL, hit_filter3, &accum, &hit) == RES_OK); 497 CHK(SVX_HIT_NONE(&hit)); 498 CHK(accum == 1); 499 500 /* Check the root node challenge */ 501 d3(r.org, 1.01, 1234, 10); 502 d3_normalize(r.dir, d3(r.dir, -1, -1, -1)); 503 CHK(RT(btree, r.org, r.dir, r.range, hit_challenge_root, NULL, NULL, &hit) 504 == RES_OK); 505 CHK(!SVX_HIT_NONE(&hit)); 506 CHK(hit.voxel.lower[0] == -1); 507 CHK(hit.voxel.upper[0] == 1); 508 CHK(IS_INF(hit.voxel.lower[1])); 509 CHK(IS_INF(hit.voxel.upper[1])); 510 CHK(IS_INF(hit.voxel.lower[2])); 511 CHK(IS_INF(hit.voxel.upper[2])); 512 CHK(hit.voxel.depth == 0); 513 CHK(hit.voxel.is_leaf == 0); 514 CHK(*((char*)hit.voxel.data) == 1); 515 CHK(eq_eps(hit.distance[0], (hit.voxel.upper[0]-r.org[0])/r.dir[0], 1.e-4)); 516 CHK(eq_eps(hit.distance[1], (hit.voxel.lower[0]-r.org[0])/r.dir[0], 1.e-4)); 517 518 image_init(NULL, &img); 519 image_init(NULL, &img2); 520 draw_image(&img, btree); 521 draw_image(&img2, btree2); 522 523 /* Check that using oct or oct2 produces effectively the same image */ 524 check_img_eq(&img, &img2); 525 526 /* Write the drawn image on stdout */ 527 CHK(image_write_ppm_stream(&img, 0/*binary*/, stdout) == RES_OK); 528 image_release(&img); 529 image_release(&img2); 530 531 CHK(svx_tree_ref_put(btree) == RES_OK); 532 CHK(svx_tree_ref_put(btree2) == RES_OK); 533 CHK(svx_device_ref_put(dev) == RES_OK); 534 CHK(mem_allocated_size() == 0); 535 return 0; 536 } 537