00001 #pragma warning ( disable : 4786 )
00002
00003 #include "octree.h"
00004
00006
00008
00009
00010
00011 #define GET_REP(a, point, Side)\
00012 \
00013 {\
00014 switch(Side) \
00015 { \
00016 case 0: \
00017 rep = point.z - a->apex.z; \
00018 break; \
00019 case 1: \
00020 rep = -point.z + a->apex.z; \
00021 break; \
00022 case 2: \
00023 rep = point.y - a->apex.y; \
00024 break; \
00025 case 3: \
00026 rep = -point.y + a->apex.y; \
00027 break; \
00028 case 4: \
00029 rep = point.x - a->apex.x; \
00030 break; \
00031 case 5: \
00032 rep = -point.x + a->apex.x; \
00033 break; \
00034 default: \
00035 exit(51); \
00036 break; \
00037 } \
00038 };
00039
00040 int last_face = -1;
00041
00042
00043 OctreeNode * assign_white_node()
00044 {
00045 OctreeNode *p;
00046
00047 p = (OctreeNode *) malloc(sizeof(OctreeNode));
00048
00049 if(p == NULL)
00050 {
00051 exit (53);
00052
00053 }
00054
00055 p->son = NULL;
00056 p->color = WHITE;
00057
00058 return(p);
00059 }
00060
00061
00062 OctreeNode * assign_black_node()
00063 {
00064 OctreeNode *p;
00065
00066 p = (OctreeNode *) malloc(sizeof(OctreeNode));
00067
00068
00069 if(p == NULL)
00070 {
00071 exit (53);
00072
00073 }
00074
00075 p->son = NULL;
00076 p->color = BLACK;
00077
00078 return(p);
00079 }
00080
00081
00082 OctreeNode * assign_gray_node(struct free_space *a,
00083 struct cubic *c, int level,int maxlevel)
00084 {
00085
00086 struct cubic sub_c;
00087 int x_mid, y_mid, z_mid;
00088 OctreeNode *p;
00089 int next_level;
00090 next_level = level +1;
00091 p = (OctreeNode *) malloc(sizeof(OctreeNode));
00092
00093 p->son = (struct child_pointer*) malloc(sizeof(struct child_pointer));
00094 if(p == NULL)
00095 {
00096 exit (53);
00097
00098 }
00099 p->color= GRAY;
00100 x_mid = (c->x1 + c->x0)/2;
00101 y_mid = (c->y1 + c->y0)/2;
00102 z_mid = (c->z1 + c->z0)/2;
00103 sub_c.x0= c->x0;
00104 sub_c.y0= c->y0;
00105 sub_c.z0= c->z0;
00106 sub_c.x1= x_mid;
00107 sub_c.y1= y_mid;
00108 sub_c.z1= z_mid;
00109 p->son->pointer[0] = construct_octree(a, &sub_c, next_level, maxlevel);
00110 p->son->pointer[0]->father= p;
00111 sub_c.x0= c->x0;
00112 sub_c.y0= c->y0;
00113 sub_c.z0= z_mid;
00114 sub_c.x1= x_mid;
00115 sub_c.y1= y_mid;
00116 sub_c.z1= c->z1;
00117 p->son->pointer[1] = construct_octree(a, &sub_c, next_level, maxlevel);
00118 p->son->pointer[1]->father= p;
00119 sub_c.x0= c->x0;
00120 sub_c.y0= y_mid;
00121 sub_c.z0= c->z0;
00122 sub_c.x1= x_mid;
00123 sub_c.y1= c->y1;
00124 sub_c.z1= z_mid;
00125 p->son->pointer[2] = construct_octree(a, &sub_c, next_level, maxlevel);
00126 p->son->pointer[2]->father= p;
00127 sub_c.x0= c->x0;
00128 sub_c.y0= y_mid;
00129 sub_c.z0= z_mid;
00130 sub_c.x1= x_mid;
00131 sub_c.y1= c->y1;
00132 sub_c.z1= c->z1;
00133 p->son->pointer[3] = construct_octree(a, &sub_c, next_level, maxlevel);
00134 p->son->pointer[3]->father= p;
00135 sub_c.x0= x_mid;
00136 sub_c.y0= c->y0;
00137 sub_c.z0= c->z0;
00138 sub_c.x1= c->x1;
00139 sub_c.y1= y_mid;
00140 sub_c.z1= z_mid;
00141 p->son->pointer[4] = construct_octree(a, &sub_c, next_level, maxlevel);
00142 p->son->pointer[4]->father= p;
00143 sub_c.x0= x_mid;
00144 sub_c.y0= c->y0;
00145 sub_c.z0= z_mid;
00146 sub_c.x1= c->x1;
00147 sub_c.y1= y_mid;
00148 sub_c.z1= c->z1;
00149 p->son->pointer[5] = construct_octree(a, &sub_c, next_level, maxlevel);
00150 p->son->pointer[5]->father= p;
00151 sub_c.x0= x_mid;
00152 sub_c.y0= y_mid;
00153 sub_c.z0= c->z0;
00154 sub_c.x1= c->x1;
00155 sub_c.y1= c->y1;
00156 sub_c.z1= z_mid;
00157 p->son->pointer[6] = construct_octree(a, &sub_c, next_level, maxlevel);
00158 p->son->pointer[6]->father= p;
00159 sub_c.x0= x_mid;
00160 sub_c.y0= y_mid;
00161 sub_c.z0= z_mid;
00162 sub_c.x1= c->x1;
00163 sub_c.y1= c->y1;
00164 sub_c.z1= c->z1;
00165 p->son->pointer[7] = construct_octree(a, &sub_c, next_level, maxlevel);
00166 p->son->pointer[7]->father= p;
00167 return(p);
00168 }
00169
00170 void mapping(struct free_space *a,
00171 struct vector_d *point,
00172 int proj_point[3])
00173
00174 {
00175 double tempi_x, tempi_y, tempi_z;
00176 double t;
00177 double d_x, d_y, d_z;
00178
00179 d_x = point->x - a->apex.x;
00180 d_y = point->y - a->apex.y;
00181 d_z = point->z - a->apex.z;
00182 if((d_x == 0) && ( d_y == 0) && (d_z ==0))
00183 {
00184 d_x = TEMP_EPSILON;
00185 d_y = TEMP_EPSILON;
00186 d_z = TEMP_EPSILON;
00187 }
00188 switch(last_face)
00189 {
00190 case 0:
00191 t = (LIMITP - a->apex.z)/d_z;
00192 if(t > 0)
00193 {
00194 tempi_x = t*d_x + a->apex.x;
00195 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00196 {
00197 tempi_y = t*d_y + a->apex.y;
00198 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00199 {
00200 last_face = proj_point[0] = 0;
00201 proj_point[1] = (int)tempi_x;
00202 proj_point[2] = (int) tempi_y;
00203 return;
00204 }
00205 }
00206 }
00207 break;
00208 case 1:
00209
00210 t = (-LIMIT - a->apex.z)/d_z;
00211 if(t > 0)
00212 {
00213 tempi_x = t*d_x + a->apex.x;
00214 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00215 {
00216 tempi_y = t*d_y + a->apex.y;
00217 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00218 {
00219 last_face = proj_point[0] = 1; proj_point[1] = (int)tempi_x;
00220 proj_point[2] = (int)tempi_y;
00221 return;
00222 }
00223 }
00224 }
00225 break;
00226 case 2:
00227
00228 t = (LIMITP - a->apex.y)/d_y;
00229 if(t > 0)
00230 {
00231 tempi_x = t*d_x + a->apex.x;
00232 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00233 {
00234 tempi_z = t*d_z + a->apex.z;
00235 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00236 {
00237 last_face = proj_point[0] = 2; proj_point[1] = (int)tempi_x;
00238 proj_point[2] = (int)tempi_z;
00239 return;
00240 }
00241 }
00242 }
00243 break;
00244 case 3:
00245
00246 t = (-LIMIT - a->apex.y)/d_y;
00247 if(t > 0)
00248 {
00249 tempi_x = t*d_x + a->apex.x;
00250 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00251 {
00252 tempi_z = t*d_z + a->apex.z;
00253 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00254 {
00255 last_face = proj_point[0] = 3; proj_point[1] = (int)tempi_x;
00256 proj_point[2] = (int)tempi_z;
00257 return;
00258 }
00259 }
00260 }
00261 break;
00262 case 4:
00263
00264 t = (LIMITP - a->apex.x)/d_x;
00265 if(t > 0)
00266 {
00267 tempi_z = t*d_z + a->apex.z;
00268 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00269 {
00270 tempi_y = t*d_y + a->apex.y;
00271 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00272 {
00273 last_face = proj_point[0] = 4; proj_point[1] = (int)tempi_y;
00274 proj_point[2] = (int)tempi_z;
00275 return;
00276 }
00277 }
00278 }
00279 break;
00280 case 5:
00281
00282 t = (-LIMIT - a->apex.x)/d_x;
00283 if(t > 0)
00284 {
00285 tempi_z = t*d_z + a->apex.z;
00286 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00287 {
00288 tempi_y = t*d_y + a->apex.y;
00289 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00290 {
00291 last_face = proj_point[0] = 5; proj_point[1] = (int)tempi_y;
00292 proj_point[2] = (int)tempi_z;
00293 return;
00294 }
00295 }
00296 }
00297 break;
00298 default:
00299 break;
00300 }
00301
00302
00303 t = (LIMITP - a->apex.z)/d_z;
00304 if(t > 0)
00305 {
00306 tempi_x = t*d_x + a->apex.x;
00307 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00308 {
00309 tempi_y = t*d_y + a->apex.y;
00310 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00311 {
00312 last_face = proj_point[0] = 0; proj_point[1] = (int)tempi_x;
00313 proj_point[2] = (int) tempi_y;
00314 return;
00315 }
00316 }
00317 }
00318
00319 t = (-LIMIT - a->apex.z)/d_z;
00320 if(t > 0)
00321 {
00322 tempi_x = t*d_x + a->apex.x;
00323 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00324 {
00325 tempi_y = t*d_y + a->apex.y;
00326 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00327 {
00328 last_face = proj_point[0] = 1; proj_point[1] = (int)tempi_x;
00329 proj_point[2] = (int)tempi_y;
00330 return;
00331 }
00332 }
00333 }
00334
00335 t = (LIMITP - a->apex.y)/d_y;
00336 if(t > 0)
00337 {
00338 tempi_x = t*d_x + a->apex.x;
00339 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00340 {
00341 tempi_z = t*d_z + a->apex.z;
00342 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00343 {
00344 last_face = proj_point[0] = 2; proj_point[1] = (int)tempi_x;
00345 proj_point[2] = (int)tempi_z;
00346 return;
00347 }
00348 }
00349 }
00350
00351 t = (-LIMIT - a->apex.y)/d_y;
00352 if(t > 0)
00353 {
00354 tempi_x = t*d_x + a->apex.x;
00355 if((tempi_x > (-DBL_LIMIT)) && (tempi_x < (DBL_LIMITP)))
00356 {
00357 tempi_z = t*d_z + a->apex.z;
00358 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00359 {
00360 last_face = proj_point[0] = 3; proj_point[1] = (int)tempi_x;
00361 proj_point[2] = (int)tempi_z;
00362 return;
00363 }
00364 }
00365 }
00366
00367 t = (LIMITP - a->apex.x)/d_x;
00368 if(t > 0)
00369 {
00370 tempi_z = t*d_z + a->apex.z;
00371 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00372 {
00373 tempi_y = t*d_y + a->apex.y;
00374 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00375 {
00376 last_face = proj_point[0] = 4; proj_point[1] = (int)tempi_y;
00377 proj_point[2] = (int)tempi_z;
00378 return;
00379 }
00380 }
00381 }
00382
00383 t = (-LIMIT - a->apex.x)/d_x;
00384 if(t > 0)
00385 {
00386 tempi_z = t*d_z + a->apex.z;
00387 if((tempi_z > (-DBL_LIMIT)) && (tempi_z < (DBL_LIMITP)))
00388 {
00389 tempi_y = t*d_y + a->apex.y;
00390 if((tempi_y > (-DBL_LIMIT)) && (tempi_y < (DBL_LIMITP)))
00391 {
00392 last_face = proj_point[0] = 5; proj_point[1] = (int)tempi_y;
00393 proj_point[2] = (int)tempi_z;
00394 return;
00395 }
00396 }
00397 }
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 if( (point->x > LIMITP ) || (point->x < -LIMIT) ||
00409 (point->y > LIMITP ) || (point->y < -LIMIT) ||
00410 (point->z > LIMITP ) || (point->z < -LIMIT) )
00411 exit (52);
00412
00413 }
00414
00415 OctreeNode *construct_octree(struct free_space *a,
00416 struct cubic *scope,
00417 int level, int maxlevel)
00418 {
00419 int i, j;
00420 int x_start, y_start, z_start, x_end, y_end, z_end;
00421 int status;
00422 int p_p[3];
00423 struct vector_d point;
00424 double rep;
00425 double side1, side2;
00426
00427 x_start = scope->x0 - LIMIT;
00428 x_end = scope->x1 - LIMIT;
00429
00430 y_start = scope->y0 - LIMIT;
00431 y_end = scope->y1 - LIMIT;
00432
00433 z_start = scope->z0 - LIMIT;
00434 z_end = scope->z1 - LIMIT;
00435
00436 if ( (a->bound_start.x > scope->x1) ||
00437 (a->bound_start.y > scope->y1) ||
00438 (a->bound_start.z > scope->z1) ||
00439 (a->bound_end.x < scope->x0) ||
00440 (a->bound_end.y < scope->y0) ||
00441 (a->bound_end.z < scope->z0) )
00442 {
00443 return(assign_black_node());
00444 }
00445 point.x = x_end;
00446 point.y = y_end;
00447 point.z = z_end;
00448
00449 mapping(a, &point, p_p);
00450
00451 GET_REP(a, point, p_p[0])
00452 if(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128] >= rep)
00453 {
00454
00455 status = INSIDE;
00456 if(level >= maxlevel)
00457 {
00458 return(assign_white_node());
00459 }
00460 }
00461 else
00462 {
00463 status = OUTSIDE;
00464 if(level >= maxlevel)
00465 {
00466 return(assign_black_node());
00467 }
00468 }
00469 side1 = z_start;
00470 point.z = side1;
00471 if( ( (status == INSIDE ) && ( a->apex.z - side1 > 0) ) ||
00472 ( (status == OUTSIDE) && ( a->apex.z - side1 < 0) ) )
00473 {
00474
00475 for(i = x_start; i < x_end; i++)
00476 {
00477 point.x = i;
00478 for(j = y_start; j < y_end; j++)
00479 {
00480 point.y = j;
00481 mapping(a, &point, p_p);
00482 GET_REP(a, point, p_p[0]);
00483 if(status^(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128]<= rep))
00484 {
00485 return(assign_gray_node(a, scope, level, maxlevel));
00486 }
00487 }
00488 }
00489 }
00490 side2 = z_end;
00491 point.z = z_end;
00492 if( ( (status == OUTSIDE) && ( a->apex.z - side2 >0) ) ||
00493 ( (status == INSIDE) && ( a->apex.z - side2 < 0) ) )
00494 {
00495
00496 for(i = x_start; i < x_end; i++)
00497 {
00498 point.x = i;
00499 for(j = y_start; j < y_end; j++)
00500 {
00501 point.y = j;
00502 mapping(a, &point, p_p);
00503 GET_REP(a, point, p_p[0]);
00504 if(status ^(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128] <= rep))
00505 {
00506 return(assign_gray_node(a, scope, level, maxlevel));
00507 }
00508 }
00509 }
00510 }
00511 point.y = side1 = y_start;
00512 if( ( (status == INSIDE) && ( a->apex.y - side1 > 0) ) ||
00513 ( (status == OUTSIDE) && ( a->apex.y - side1 <0) ) )
00514 {
00515
00516 for(i = x_start; i < x_end; i++)
00517 {
00518 point.x = i;
00519 for(j = z_start; j < z_end; j++)
00520 {
00521 point.z = j;
00522
00523 mapping(a, &point, p_p);
00524
00525 GET_REP(a, point, p_p[0]);
00526 if(status ^ (a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128]<=rep))
00527 {
00528 return(assign_gray_node(a, scope, level, maxlevel));
00529 }
00530 }
00531 }
00532 }
00533 point.y = side2 = y_end;
00534 if( ( (status == OUTSIDE) && ( a->apex.y - side2 > 0) ) ||
00535 ( (status == INSIDE) && ( a->apex.y - side2 <0) ) )
00536 {
00537
00538 for(i = x_start; i < x_end; i++)
00539 {
00540 point.x = i;
00541 for(j = z_start; j < z_end; j++)
00542 {
00543 point.z = j;
00544
00545 mapping(a, &point, p_p);
00546 GET_REP(a, point, p_p[0]);
00547 if(status^(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128]<=rep))
00548 {
00549 return(assign_gray_node(a, scope, level, maxlevel));
00550 }
00551 }
00552 }
00553 }
00554 point.x = side1 = x_start;
00555 if( ( (status == INSIDE) && ( a->apex.x - side1 > 0) ) ||
00556 ( (status == OUTSIDE) && ( a->apex.x - side1 <0) ) )
00557 {
00558
00559 for(i = y_start; i < y_end; i++)
00560 {
00561 point.y = i;
00562 for(j = z_start; j < z_end; j++)
00563 {
00564 point.z = j;
00565
00566 mapping(a, &point, p_p);
00567 GET_REP(a, point, p_p[0]);
00568 if(status^(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128]<=rep))
00569 {
00570 return(assign_gray_node(a, scope, level, maxlevel));
00571 }
00572 }
00573 }
00574 }
00575 point.x = side2 = x_end;
00576
00577 if( ( (status == OUTSIDE) && ( a->apex.x - side2 > 0) ) ||
00578 ( (status == INSIDE) && ( a->apex.x - side2 <0) ) )
00579 {
00580
00581 for(i = y_start; i < y_end; i++)
00582 {
00583 point.y = i;
00584 for(j = z_start; j < z_end; j++)
00585 {
00586 point.z = j;
00587 mapping(a, &point, p_p);
00588 GET_REP(a, point, p_p[0]);
00589 if(status^(a->scaled_distance[p_p[0]][p_p[1]+128][p_p[2]+128]<=rep))
00590 {
00591 return(assign_gray_node(a, scope, level, maxlevel));
00592 }
00593 }
00594 }
00595 }
00596 if(status == INSIDE)
00597 {
00598 return(assign_white_node());
00599 }
00600 else
00601 {
00602 if( ((a->apex.x + LIMIT)<=scope->x1) && ((a->apex.x + LIMIT)>=scope->x0) &&
00603 ((a->apex.y + LIMIT)<=scope->y1) && ((a->apex.y + LIMIT)>=scope->y0) &&
00604 ((a->apex.z + LIMIT)<=scope->z1) && ((a->apex.z + LIMIT)>=scope->z0))
00605 {
00606 return(assign_gray_node(a, scope, level, maxlevel));
00607 }
00608 else
00609 {
00610 return(assign_black_node());
00611 }
00612 }
00613 }
00614
00615
00616 inline void scale_distance_assign(int proj_p[3], int i, int j, struct free_space *a, Range_Sensor* camera)
00617 {
00618 switch (proj_p[0])
00619 {
00620 case 0:
00621 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00622 (camera->ray_coord_map[i][j][2] - a->apex.z);
00623 break;
00624 case 1:
00625 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00626 -(camera->ray_coord_map[i][j][2] - a->apex.z);
00627 break;
00628 case 2:
00629 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00630 (camera->ray_coord_map[i][j][1] - a->apex.y);
00631 break;
00632 case 3:
00633 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00634 -(camera->ray_coord_map[i][j][1] - a->apex.y);
00635 break;
00636 case 4:
00637 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00638 (camera->ray_coord_map[i][j][0] - a->apex.x);
00639 break;
00640 case 5:
00641 a->scaled_distance[proj_p[0]][proj_p[1]+128][proj_p[2]+128] = (short)
00642 -(camera->ray_coord_map[i][j][0] - a->apex.x);
00643 break;
00644 default:
00645 exit(3);
00646
00647 }
00648 }
00649
00650
00651 void calculate_scaled_distance(struct free_space *a, Range_Sensor* camera)
00652 {
00653 int i, j;
00654 int proj_point[3];
00655 struct vector_d point;
00656 struct vector_d max, min;
00657
00658
00659 memset (&a->scaled_distance[0][0][0],0,sizeof(a->scaled_distance));
00660
00661 max.x = min.x = a->apex.x;
00662 max.y = min.y = a->apex.y;
00663 max.z = min.z = a->apex.z;
00664
00665 for(i = 0; i < 256; i++)
00666 {
00667 for(j = 0; j < 256; j++)
00668 {
00669 point.x = camera->ray_coord_map[i][j][0];
00670 point.y = camera->ray_coord_map[i][j][1];
00671 point.z = camera->ray_coord_map[i][j][2];
00672
00673 if( (point.x == 0) && (point.y == 0) && (point.z == 0))
00674 continue;
00675 if(point.x > max.x) max.x = point.x;
00676 if(point.y > max.y) max.y = point.y;
00677 if(point.z > max.z) max.z = point.z;
00678 if(point.x < min.x) min.x = point.x;
00679 if(point.y < min.y) min.y = point.y;
00680 if(point.z < min.z) min.z = point.z;
00681
00682 mapping(a, &point, proj_point);
00683 scale_distance_assign(proj_point, i, j, a, camera);
00684
00685
00686 }
00687 }
00688 a->bound_start.x = min.x + LIMIT;
00689 a->bound_start.y = min.y + LIMIT;
00690 a->bound_start.z = min.z + LIMIT;
00691 a->bound_end.x = max.x + LIMIT;
00692 a->bound_end.y = max.y + LIMIT;
00693 a->bound_end.z = max.z + LIMIT;
00694 }