scad_geometry.c (94751B)
1 /* Copyright (C) 2022-2024 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #include "scad.h" 17 #include "scad_c.h" 18 #include "scad_device.h" 19 #include "scad_geometry.h" 20 21 #include <rsys/rsys.h> 22 #include <rsys/mem_allocator.h> 23 #include <rsys/str.h> 24 #include <rsys/math.h> 25 #include <rsys/double2.h> 26 #include <rsys/double3.h> 27 #include <rsys/logger.h> 28 #include <rsys/hash_table.h> 29 30 #include <stdlib.h> 31 #include <string.h> 32 #include <gmsh/gmshc.h> 33 34 /* A type used to deduplicate tags */ 35 #define HTABLE_NAME tags 36 #define HTABLE_KEY int 37 #define HTABLE_DATA char 38 #include <rsys/hash_table.h> 39 40 /* A type used for mappings */ 41 #define HTABLE_NAME mappings 42 #define HTABLE_KEY int 43 #define HTABLE_DATA size_t 44 #include <rsys/hash_table.h> 45 46 #include <limits.h> 47 48 /******************************************************************************* 49 * Utility functions 50 ******************************************************************************/ 51 struct coord_pair { 52 const double* x; 53 const double* y; 54 }; 55 56 static res_T 57 geom_set_name 58 (struct scad_geometry* geom, 59 const struct str* name) /* Can be NULL but not "" */ 60 { 61 struct scad_device* dev = get_device(); 62 int same_name, name_isnt_null, previous_name_is_null; 63 res_T res = RES_OK; 64 65 ASSERT(geom); 66 ASSERT(!name || !str_is_empty(name)); 67 68 previous_name_is_null = str_is_empty(&geom->name); 69 if(name) { 70 name_isnt_null = 1; 71 same_name = (!previous_name_is_null) && (0 == str_cmp(name, &geom->name)); 72 } else { 73 name_isnt_null = 0; 74 same_name = previous_name_is_null; 75 } 76 77 if(same_name) /* No change needed */ 78 goto exit; 79 80 if(name_isnt_null) { 81 struct scad_geometry** g 82 = htable_names_find(&dev->geometry_names, name); 83 if(g) { /* if defined, names must be unique */ 84 res = RES_BAD_ARG; 85 log_error(get_device(), "Geometry name '%s' is allready in use.\n", 86 str_cget(name)); 87 goto error; 88 } 89 } 90 91 if(!previous_name_is_null) { 92 size_t n = htable_names_erase(&dev->geometry_names, &geom->name); 93 ASSERT((n == 1)); (void)n; 94 } 95 if(name_isnt_null) { 96 ERR(str_copy(&geom->name, name)); 97 ERR(htable_names_set(&dev->geometry_names, name, &geom)); 98 } else { 99 str_clear(&geom->name); 100 } 101 102 exit: 103 return res; 104 error: 105 goto exit; 106 } 107 108 static int 109 mixed_dim_err_msg 110 (const char* op, 111 struct scad_geometry** geometries, 112 const size_t geometries_count, 113 int* dim) 114 { 115 size_t i, j; 116 struct scad_device* dev = get_device(); 117 ASSERT(op && (geometries || geometries_count == 0) && dim); 118 for(i = 0; i < geometries_count; i++) { 119 for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) { 120 int d = geometries[i]->gmsh_dimTags[j]; 121 if(*dim == INT_MAX) *dim = d; 122 else if (*dim != d) { 123 log_error(dev, 124 "Dimension mismatch in %s operation creating geometry.\n", op); 125 return 1; 126 } 127 } 128 } 129 return 0; 130 } 131 132 static res_T 133 geometry_create 134 (struct scad_geometry** out_geometry) 135 { 136 res_T res = RES_OK; 137 struct scad_geometry* geom = NULL; 138 struct scad_device* dev = get_device(); 139 struct mem_allocator* allocator = dev->allocator; 140 char one = 1; 141 142 ASSERT(out_geometry); 143 144 geom = (struct scad_geometry*)MEM_CALLOC(allocator, 1, sizeof(*geom)); 145 if(!geom) { 146 res = RES_MEM_ERR; 147 goto error; 148 } 149 150 ref_init(&geom->ref); 151 str_init(allocator, &geom->name); 152 ERR(htable_geometries_set(&dev->allgeom, &geom, &one)); 153 154 if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) { 155 if(str_is_empty(&geom->name)) { 156 logger_print(dev->logger, dev->log_type, 157 "Creating unnamed geometry %p (count set to 1).\n", (void*)geom); 158 } else { 159 logger_print(dev->logger, dev->log_type, 160 "Creating geometry '%s' (%p, count set to 1).\n", 161 str_cget(&geom->name), (void*)geom); 162 } 163 } 164 165 end: 166 *out_geometry = geom; 167 return res; 168 error: 169 if(geom) { 170 SCAD(geometry_ref_put(geom)); 171 geom = NULL; 172 } 173 goto end; 174 } 175 176 enum tag_operation { 177 COMMON_TAGS, 178 UNIQUE_TAGS 179 }; 180 181 static res_T 182 process_tag_list 183 (const int* dt1, 184 const size_t c1, 185 const int* dt2, 186 const size_t c2, 187 const enum tag_operation op, 188 int** out_dimTags, 189 size_t* out_dimTags_n) 190 { 191 res_T res = RES_OK; 192 size_t i, d, c = 0, sz = 0; 193 struct scad_device* dev = get_device(); 194 struct mem_allocator* allocator = dev->allocator; 195 struct htable_tags t2, t3; 196 struct htable_tags_iterator it, end; 197 int* dimTags = NULL; 198 const char set1 = BIT(0), set2 = BIT(1), both = set1 | set2; 199 200 ASSERT((dt1 || c1 == 0) && (dt2 || c2 == 0) && out_dimTags && out_dimTags_n); 201 202 htable_tags_init(allocator, &t2); 203 htable_tags_init(allocator, &t3); 204 205 /* list tags and flags them whith set(s) they are from */ 206 for(i = 0; i < c1; i += 2) { 207 int dim = dt1[i]; 208 int tag = dt1[i+1]; 209 struct htable_tags* tn = (dim == 2) ? &t2 : &t3; 210 ASSERT(dim == 2 || dim == 3); 211 ERR(htable_tags_set(tn, &tag, &set1)); 212 } 213 for(i = 0; i < c2; i += 2) { 214 char *fl, tmp; 215 int dim = dt2[i]; 216 int tag = dt2[i+1]; 217 struct htable_tags* tn = (dim == 2) ? &t2 : &t3; 218 ASSERT(dim == 2 || dim == 3); 219 fl = htable_tags_find(tn, &tag); 220 if(fl == NULL) { 221 tmp = set2; 222 fl = &tmp; 223 } else { 224 *fl |= set2; 225 } 226 ERR(htable_tags_set(tn, &tag, fl)); 227 } 228 229 switch(op) { 230 case COMMON_TAGS: 231 for(d = 2; d <= 3; d++) { 232 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 233 htable_tags_begin(tn, &it); 234 htable_tags_end(tn, &end); 235 while(!htable_tags_iterator_eq(&it, &end)) { 236 if(*htable_tags_iterator_data_get(&it) == both) sz++; 237 htable_tags_iterator_next(&it); 238 } 239 } 240 break; 241 case UNIQUE_TAGS: 242 sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3); 243 break; 244 default: FATAL("Invalid enum value"); 245 } 246 247 /* Build result */ 248 if(sz > 0) { 249 dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags)); 250 if(!dimTags) { 251 res = RES_MEM_ERR; 252 goto error; 253 } 254 255 for(d = 2; d <= 3; d++) { 256 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 257 htable_tags_begin(tn, &it); 258 htable_tags_end(tn, &end); 259 while(!htable_tags_iterator_eq(&it, &end)) { 260 int add; 261 switch(op) { 262 case COMMON_TAGS: 263 add = (*htable_tags_iterator_data_get(&it) == both); 264 break; 265 case UNIQUE_TAGS: 266 add = 1; 267 break; 268 default: FATAL("Invalid enum value"); 269 } 270 if(add) { 271 dimTags[c++] = 2; 272 dimTags[c++] = *htable_tags_iterator_key_get(&it); 273 } 274 htable_tags_iterator_next(&it); 275 } 276 } 277 ASSERT(sz*2 == c); 278 } 279 280 exit: 281 *out_dimTags_n = c; 282 *out_dimTags = dimTags; 283 htable_tags_release(&t2); 284 htable_tags_release(&t3); 285 return res; 286 error: 287 MEM_RM(allocator, dimTags); 288 dimTags = NULL; 289 c = 0; 290 goto exit; 291 } 292 293 static res_T 294 gather_tags 295 (struct scad_geometry** geometries, 296 const size_t geometries_count, 297 int** out_dimTags, 298 size_t* out_dimTags_n) 299 { 300 res_T res = RES_OK; 301 int* dimTags = NULL; 302 size_t i, j, c, sz; 303 struct scad_device* dev = get_device(); 304 struct mem_allocator* allocator = dev->allocator; 305 struct htable_tags t2, t3; 306 struct htable_tags_iterator it, end; 307 308 ASSERT((geometries || !geometries_count) && out_dimTags && out_dimTags_n); 309 310 htable_tags_init(allocator, &t2); 311 htable_tags_init(allocator, &t3); 312 313 /* list tags and remove duplicates */ 314 for(i = 0; i < geometries_count; i++) { 315 if(!geometries[i]) { 316 res = RES_BAD_ARG; 317 goto error; 318 } 319 for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) { 320 char one = 1; 321 int dim = geometries[i]->gmsh_dimTags[j]; 322 int tag = geometries[i]->gmsh_dimTags[j+1]; 323 struct htable_tags* tn = (dim == 2) ? &t2 : &t3; 324 ASSERT(dim == 2 || dim == 3); 325 ERR(htable_tags_set(tn, &tag, &one)); 326 } 327 } 328 329 /* Build result */ 330 sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3); 331 dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags)); 332 if(!dimTags) { 333 res = RES_MEM_ERR; 334 goto error; 335 } 336 337 c = 0; 338 htable_tags_begin(&t2, &it); 339 htable_tags_end(&t2, &end); 340 while(!htable_tags_iterator_eq(&it, &end)) { 341 dimTags[c++] = 2; 342 dimTags[c++] = *htable_tags_iterator_key_get(&it); 343 htable_tags_iterator_next(&it); 344 } 345 htable_tags_begin(&t3, &it); 346 htable_tags_end(&t3, &end); 347 while(!htable_tags_iterator_eq(&it, &end)) { 348 dimTags[c++] = 3; 349 dimTags[c++] = *htable_tags_iterator_key_get(&it); 350 htable_tags_iterator_next(&it); 351 } 352 ASSERT(sz*2 == c); 353 354 *out_dimTags_n = c; 355 *out_dimTags = dimTags; 356 357 exit: 358 htable_tags_release(&t2); 359 htable_tags_release(&t3); 360 return res; 361 error: 362 MEM_RM(allocator, dimTags); 363 goto exit; 364 } 365 366 static res_T 367 store_tags 368 (int* dimTags, 369 size_t count, 370 const int down_to_dim, 371 struct htable_tags t[4]) 372 { 373 res_T res = RES_OK; 374 char one = 1; 375 size_t i; 376 ASSERT((dimTags || count == 0) && t); 377 for(i = 0; i < count; i += 2) { 378 int dim = dimTags[i]; 379 int tag = dimTags[i+1]; 380 ASSERT(dim >= 0 && dim <= 3); 381 if(down_to_dim > dim) continue; 382 ERR(htable_tags_set(t+dim, &tag, &one)); 383 } 384 385 exit: 386 return res; 387 error: 388 goto exit; 389 } 390 391 /* Recursivelly get the tags of geometries, possibly down to dim 0. 392 * Trigger a call to sync_device() */ 393 static res_T 394 gather_tags_recursive 395 (struct scad_geometry** geometries, 396 const size_t geometries_count, 397 const int down_to_dim, 398 int* out_dimTags[4], 399 size_t out_dimTags_n[4]) 400 { 401 res_T res = RES_OK; 402 int* dimTags[4] = { NULL, NULL, NULL, NULL }, *sub = NULL; 403 size_t i, sz[4]; 404 struct scad_device* dev = get_device(); 405 struct mem_allocator* allocator = dev->allocator; 406 struct htable_tags t[4]; 407 struct htable_tags_iterator it, end; 408 int dim; 409 410 ASSERT((geometries || geometries_count == 0) && out_dimTags && out_dimTags_n); 411 ASSERT(0 <= down_to_dim && down_to_dim <= 3); 412 413 for(i = (size_t)down_to_dim; i < 4; i++) { 414 htable_tags_init(allocator, t+i); 415 } 416 417 /* list tags by dimension and remove duplicates */ 418 for(i = 0; i < geometries_count; i++) { 419 if(!geometries[i]) { 420 res = RES_BAD_ARG; 421 goto error; 422 } 423 ERR(store_tags(geometries[i]->gmsh_dimTags, geometries[i]->gmsh_dimTags_n, 424 down_to_dim, t)); 425 } 426 427 /* Recursively build result by dimension and list constituents, 428 * begining with dim==3 */ 429 ERR(sync_device()); 430 for(dim = 3; dim >= down_to_dim; dim--) { 431 size_t c = 0; 432 sz[dim] = 2 * htable_tags_size_get(t+dim); 433 if(sz[dim] == 0) 434 continue; 435 dimTags[dim] = MEM_ALLOC(allocator, sz[dim] * sizeof(*dimTags)); 436 if(!dimTags[dim]) { 437 res = RES_MEM_ERR; 438 goto error; 439 } 440 htable_tags_begin(t+dim, &it); 441 htable_tags_end(t+dim, &end); 442 while(!htable_tags_iterator_eq(&it, &end)) { 443 dimTags[dim][c++] = dim; 444 dimTags[dim][c++] = *htable_tags_iterator_key_get(&it); 445 htable_tags_iterator_next(&it); 446 } 447 if(dim > down_to_dim) { 448 int ierr; 449 size_t subn; 450 gmshModelGetBoundary(dimTags[dim], sz[dim], &sub, &subn, 0, 0, 0, &ierr); 451 ERR(gmsh_err_to_res_T(ierr)); 452 ERR(store_tags(sub, subn, down_to_dim, t)); 453 gmshFree(sub); sub = NULL; 454 } 455 ASSERT(sz[dim] == c); 456 } 457 458 for(i = (size_t)down_to_dim; i < 4; i++) { 459 out_dimTags_n[i] = sz[i]; 460 out_dimTags[i] = dimTags[i]; 461 } 462 463 exit: 464 if(sub) gmshFree(sub); 465 for(i = (size_t)down_to_dim; i < 4; i++) { 466 htable_tags_release(t+i); 467 } 468 return res; 469 error: 470 for(i = (size_t)down_to_dim; i < 4; i++) { 471 MEM_RM(allocator, dimTags[i]); 472 } 473 goto exit; 474 } 475 476 static double size_callback 477 (int dim, int tag, double x, double y, double z, double lc, void* data_) 478 { 479 struct scad_device* dev = get_device(); 480 double *modifier = 481 htable_size_modifiers_find(dev->size_modifiers_by_dim + dim, &tag); 482 (void)x;(void)y;(void)z;(void)data_; 483 if(!modifier) 484 return lc; /* No modifier defined */ 485 if(*modifier < 0) 486 return -(*modifier); /* Absolute size modifier */ 487 return (*modifier) * lc; /* Size factor modifier */ 488 } 489 490 /* gmsh documentation states that memory allocated by gmsh should be freed using 491 * gmshFree. 492 * According to valgrind map results as allocated by gmsh are not fully freed if 493 * simply freed using gmshFree. 494 * A code review shows that maps content is recursivelly allocated. 495 * We end writting this helper function that free maps content and makes 496 * valgrind happy. */ 497 static void 498 free_gmsh_map 499 (int** map, 500 size_t mapnn) 501 { 502 size_t i; 503 if(!mapnn) return; 504 for(i = 0; i < mapnn; i++) { 505 gmshFree(map[i]); 506 } 507 gmshFree(map); 508 } 509 510 /******************************************************************************* 511 * Local functions 512 ******************************************************************************/ 513 static void 514 geometry_release(ref_T* ref) 515 { 516 struct scad_geometry* geom; 517 struct scad_device* dev = get_device(); 518 struct mem_allocator* allocator = dev->allocator; 519 size_t n; 520 ASSERT(ref); 521 522 geom = CONTAINER_OF(ref, struct scad_geometry, ref); 523 CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom)); 524 MEM_RM(allocator, geom->gmsh_dimTags); 525 if(!str_is_empty(&geom->name)) { 526 n = htable_names_erase(&dev->geometry_names, &geom->name); 527 ASSERT(n == 1); 528 } 529 str_release(&geom->name); 530 n = htable_geometries_erase(&dev->allgeom, &geom); 531 ASSERT(n == 1); (void)n; 532 if(geom->release && geom->custom) { 533 (*geom->release)(geom->custom); 534 } 535 MEM_RM(allocator, geom); 536 } 537 538 /****************************************************************************** 539 * Exported functions 540 *****************************************************************************/ 541 res_T 542 scad_geometry_ref_get 543 (struct scad_geometry* geom) 544 { 545 res_T res = RES_OK; 546 struct scad_device* dev = get_device(); 547 548 if(!geom) return RES_BAD_ARG; 549 ERR(check_device(FUNC_NAME)); 550 551 ref_get(&geom->ref); 552 if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) { 553 if(str_is_empty(&geom->name)) { 554 logger_print(dev->logger, dev->log_type, 555 "Getting a reference on unnamed geometry %p (count set to %lu).\n", 556 (void*)geom, (long unsigned)geom->ref); 557 } else { 558 logger_print(dev->logger, dev->log_type, 559 "Getting a reference on geometry '%s' (count set to %lu).\n", 560 str_cget(&geom->name), (long unsigned)geom->ref); 561 } 562 } 563 564 end: 565 return res; 566 error: 567 goto end; 568 } 569 570 res_T 571 scad_geometry_ref_put 572 (struct scad_geometry* geom) 573 { 574 res_T res = RES_OK; 575 struct scad_device* dev = get_device(); 576 577 if(!geom) return RES_BAD_ARG; 578 ERR(check_device(FUNC_NAME)); 579 580 if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) { 581 if(str_is_empty(&geom->name)) { 582 logger_print(dev->logger, dev->log_type, 583 "Putting a reference on unnamed geometry %p (count set to %lu).\n", 584 (void*)geom, (long unsigned)geom->ref - 1); 585 } else { 586 logger_print(dev->logger, dev->log_type, 587 "Putting a reference on geometry '%s' (count set to %lu).\n", 588 str_cget(&geom->name), (long unsigned)geom->ref - 1); 589 } 590 } 591 ref_put(&geom->ref, geometry_release); 592 593 end: 594 return res; 595 error: 596 goto end; 597 } 598 599 res_T 600 scad_scene_count 601 (size_t* count) 602 { 603 res_T res = RES_OK; 604 struct scad_device* dev = get_device(); 605 606 if(!count) { 607 res =RES_BAD_ARG; 608 goto error; 609 } 610 611 ERR(check_device(FUNC_NAME)); 612 613 *count = htable_geometries_size_get(&dev->allgeom); 614 615 end: 616 return res; 617 error: 618 goto end; 619 } 620 621 res_T 622 scad_scene_clear 623 (void) 624 { 625 res_T res = RES_OK; 626 int ierr; 627 struct htable_geometries tmp; 628 struct htable_geometries_iterator it, end; 629 struct scad_device* dev = get_device(); 630 struct mem_allocator* allocator = NULL; 631 int log, empty; 632 enum scad_log_refcounting option; 633 enum log_type log_type; 634 635 ERR(check_device(FUNC_NAME)); 636 allocator = dev->allocator; 637 option = dev->options.Misc.LogRefCounting; 638 639 htable_geometries_init(allocator, &tmp); 640 ERR(htable_geometries_copy(&tmp, &dev->allgeom)); 641 htable_geometries_begin(&tmp, &it); 642 htable_geometries_end(&tmp, &end); 643 empty = htable_geometries_is_empty(&dev->allgeom); 644 log_type = empty ? LOG_OUTPUT : LOG_WARNING; 645 log = (option & SCAD_LOG_DIMTAGS_ALL) 646 || (!empty && (option & SCAD_LOG_DIMTAGS_ONLY_UNDELETED)); 647 SWAP(int, dev->log, log); 648 SWAP(enum log_type, dev->log_type, log_type); 649 if(dev->log) { 650 logger_print(dev->logger, dev->log_type, "Clearing scene.\n"); 651 if(empty) { 652 logger_print(dev->logger, dev->log_type, "scene is empty.\n"); 653 } 654 } 655 while(!htable_geometries_iterator_eq(&it, &end)) { 656 struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it); 657 long cpt = geom->ref; 658 while(cpt-- > 0) { 659 ERR(scad_geometry_ref_put(geom)); 660 } 661 htable_geometries_iterator_next(&it); 662 } 663 if(dev->log) { 664 logger_print(dev->logger, dev->log_type, "End clearing scene.\n"); 665 } 666 SWAP(int, log, dev->log); 667 SWAP(enum log_type, log_type, dev->log_type); 668 669 /* Ensure clear is complete (not scad-registered tags) */ 670 gmshClear(&ierr); 671 ERR(gmsh_err_to_res_T(ierr)); 672 673 end: 674 if(allocator) htable_geometries_release(&tmp); 675 return res; 676 error: 677 goto end; 678 } 679 680 res_T 681 scad_geometry_get_count 682 (const struct scad_geometry* geom, 683 size_t* count) 684 { 685 res_T res = RES_OK; 686 687 if(!geom || !count) { 688 res = RES_BAD_ARG; 689 goto error; 690 } 691 692 ERR(check_device(FUNC_NAME)); 693 694 ASSERT(geom->gmsh_dimTags_n % 2 == 0); 695 *count = geom->gmsh_dimTags_n / 2; 696 697 exit: 698 return res; 699 error: 700 goto exit; 701 } 702 703 res_T 704 scad_geometry_is_empty 705 (const struct scad_geometry* geom, 706 int* empty) 707 { 708 res_T res = RES_OK; 709 710 if(!geom || !empty) { 711 res = RES_BAD_ARG; 712 goto error; 713 } 714 715 ERR(check_device(FUNC_NAME)); 716 717 *empty = (geom->gmsh_dimTags_n == 0); 718 719 exit: 720 return res; 721 error: 722 goto exit; 723 } 724 725 res_T 726 scad_geometry_set_custom_data 727 (struct scad_geometry* geom, 728 void (*release) (void* data), 729 void* data) 730 { 731 res_T res = RES_OK; 732 733 if(!geom) { 734 res = RES_BAD_ARG; 735 goto error; 736 } 737 738 ERR(check_device(FUNC_NAME)); 739 740 geom->custom = data; 741 geom->release = release; 742 743 exit: 744 return res; 745 error: 746 goto exit; 747 } 748 749 res_T 750 scad_geometry_get_custom_data 751 (struct scad_geometry* geom, 752 void** data) 753 { 754 res_T res = RES_OK; 755 756 if(!geom || !data) { 757 res = RES_BAD_ARG; 758 goto error; 759 } 760 761 ERR(check_device(FUNC_NAME)); 762 763 if(geom->release && geom->custom) { 764 (*geom->release)(geom->custom); 765 } 766 *data = geom->custom; 767 768 exit: 769 return res; 770 error: 771 goto exit; 772 } 773 774 res_T 775 scad_geometry_set_visibility 776 (const struct scad_geometry* geom, 777 int visible, 778 int recursive) 779 { 780 res_T res = RES_OK; 781 int ierr; 782 int* data = NULL; 783 size_t sz; 784 785 if(!geom) { 786 res = RES_BAD_ARG; 787 goto error; 788 } 789 790 ERR(check_device(FUNC_NAME)); 791 792 data = geom->gmsh_dimTags; 793 sz = geom->gmsh_dimTags_n; 794 795 ERR(sync_device()); 796 gmshModelSetVisibility(data, sz, visible, recursive, &ierr); 797 ERR(gmsh_err_to_res_T(ierr)); 798 799 exit: 800 return res; 801 error: 802 goto exit; 803 } 804 805 res_T 806 scad_geometries_clear_mesh 807 (struct scad_geometry** geometries, 808 const size_t geometries_count) 809 { 810 res_T res = RES_OK; 811 int ierr, dim; 812 struct scad_device* dev = get_device(); 813 struct mem_allocator* allocator = NULL; 814 size_t sz[4]; 815 int *data[4] = { NULL, NULL, NULL, NULL}; 816 817 if(!geometries || geometries_count == 0) { 818 res = RES_BAD_ARG; 819 goto error; 820 } 821 822 ERR(check_device(FUNC_NAME)); 823 allocator = dev->allocator; 824 825 ERR(gather_tags_recursive(geometries, geometries_count, 0, data, sz)); 826 for(dim = 3; dim >= 0; dim--) { 827 /* Cannot clear the mesh of lower dim entities if linked to higher dim 828 * entities with uncleared mesh: start from the higher dim down to 0 */ 829 gmshModelMeshClear(data[dim], sz[dim], &ierr); 830 ERR(gmsh_err_to_res_T(ierr)); 831 } 832 833 exit: 834 if(allocator) { 835 for(dim = 3; dim >= 0; dim--) { 836 MEM_RM(allocator, data[dim]); 837 } 838 } 839 return res; 840 error: 841 goto exit; 842 } 843 844 res_T 845 scad_geometry_get_name 846 (const struct scad_geometry* geom, 847 const char** name) 848 { 849 res_T res = RES_OK; 850 851 if(!geom || !name) { 852 res = RES_BAD_ARG; 853 goto error; 854 } 855 856 ERR(check_device(FUNC_NAME)); 857 858 if(str_is_empty(&geom->name)) { 859 *name = NULL; 860 } else { 861 *name = str_cget(&geom->name); 862 } 863 864 exit: 865 return res; 866 error: 867 goto exit; 868 } 869 870 res_T 871 scad_geometry_get_mass 872 (struct scad_geometry* geom, 873 double* mass) 874 { 875 res_T res = RES_OK; 876 int ref_dim = 0; 877 size_t i; 878 int* data = NULL; 879 size_t sz = 0; 880 double m = 0; 881 882 if(!geom || !mass) { 883 res = RES_BAD_ARG; 884 goto error; 885 } 886 887 ERR(check_device(FUNC_NAME)); 888 889 data = geom->gmsh_dimTags; 890 sz = geom->gmsh_dimTags_n; 891 892 ref_dim = data[0]; 893 for(i = 0; i < sz; i += 2) { 894 double geom_mass; 895 int dim = data[i], tag = data[i+1]; 896 int ierr = 0; 897 if(ref_dim != dim) goto error; 898 gmshModelOccGetMass(dim, tag, &geom_mass, &ierr); 899 ERR(gmsh_err_to_res_T(ierr)); 900 m += geom_mass; 901 } 902 *mass = m; 903 904 exit: 905 return res; 906 error: 907 goto exit; 908 } 909 910 res_T 911 scad_geometry_get_closest_point 912 (struct scad_geometry* geom, 913 const double from[3], 914 double closest[3], 915 double* closest_distance, 916 struct scad_geometry** out_geometry) 917 { 918 res_T res = RES_OK; 919 size_t i = 0; 920 double* coord = NULL; 921 double* pcoord = NULL; 922 double tmp[3], min[3], min_d = DBL_MAX; 923 struct scad_device* dev = get_device(); 924 struct darray_int tags; 925 const int* data = NULL; 926 size_t sz = 0; 927 int initialized = 0, min_tag = -1; 928 struct scad_geometry* out = NULL; 929 930 if(!geom || !from || !closest || !closest_distance) { 931 res = RES_BAD_ARG; 932 goto error; 933 } 934 935 ERR(check_device(FUNC_NAME)); 936 937 darray_int_init(dev->allocator, &tags); 938 initialized = 1; 939 940 ERR(get_2d_tags(geom, &tags)); 941 data = darray_int_cdata_get(&tags); 942 sz = darray_int_size_get(&tags); 943 944 for(i = 0; i < sz; i++) { 945 double d; 946 int ierr = 0; 947 int dim = 2; 948 int tag = data[i]; 949 size_t pcoord_n; 950 size_t coord_n; 951 952 gmshModelGetClosestPoint(dim, tag, from, 3, &coord, &coord_n, &pcoord, 953 &pcoord_n, &ierr); 954 ERR(gmsh_err_to_res_T(ierr)); 955 ASSERT(pcoord_n == (size_t)dim); 956 ASSERT(coord_n == 3); 957 d = d3_len(d3_sub(tmp, from, coord)); 958 if(d < min_d) { 959 min_tag = tag; 960 min_d = d; 961 d3_set(min, coord); 962 } 963 gmshFree(coord); 964 gmshFree(pcoord); 965 coord = pcoord = NULL; 966 } 967 ASSERT(min_tag != -1); 968 d3_set(closest, min); 969 *closest_distance = min_d; 970 if(out_geometry) { 971 ERR(geometry_create(&out)); 972 out->gmsh_dimTags = 973 MEM_ALLOC(dev->allocator, 2 * sizeof(*out->gmsh_dimTags)); 974 if(!out->gmsh_dimTags) { 975 res = RES_MEM_ERR; 976 goto error; 977 } 978 out->gmsh_dimTags_n = 2; 979 out->gmsh_dimTags[0] = 2; 980 out->gmsh_dimTags[1] = min_tag; 981 ERR(device_register_tags(out)); 982 } 983 984 exit: 985 if(initialized) darray_int_release(&tags); 986 if(out_geometry) *out_geometry = out; 987 gmshFree(coord); 988 gmshFree(pcoord); 989 return res; 990 error: 991 if(out) { 992 SCAD(geometry_ref_put(out)); 993 out = NULL; 994 } 995 goto exit; 996 } 997 998 res_T 999 scad_geometry_get_centerofmass 1000 (struct scad_geometry* geom, 1001 double center[3]) 1002 { 1003 res_T res = RES_OK; 1004 double *centers = NULL, *masses = NULL; 1005 size_t i = 0, count; 1006 struct mem_allocator* allocator; 1007 int ierr; 1008 1009 if(!geom || !center) { 1010 res = RES_BAD_ARG; 1011 goto error; 1012 } 1013 1014 ERR(check_device(FUNC_NAME)); 1015 1016 ASSERT(geom->gmsh_dimTags_n % 2 == 0); 1017 count = geom->gmsh_dimTags_n / 2; 1018 if(count == 1) { 1019 int dim = geom->gmsh_dimTags[i]; 1020 int tag = geom->gmsh_dimTags[i + 1]; 1021 gmshModelOccGetCenterOfMass(dim, tag, center, center+1, center+2, &ierr); 1022 ERR(gmsh_err_to_res_T(ierr)); 1023 } else { 1024 double c[3] = { 0, 0, 0 }, tmp[3], m = 0; 1025 allocator = get_device()->allocator; 1026 centers = MEM_ALLOC(allocator, count * 3 * sizeof(*centers)); 1027 masses = MEM_ALLOC(allocator, count * sizeof(*masses)); 1028 if(!centers || !masses) { 1029 res = RES_MEM_ERR; 1030 goto error; 1031 } 1032 for(i = 0; i < count; i++) { 1033 int dim = geom->gmsh_dimTags[2*i]; 1034 int tag = geom->gmsh_dimTags[2*i + 1]; 1035 gmshModelOccGetCenterOfMass(dim, tag, 1036 centers+3*i, centers+3*i+1, centers+3*i+2, &ierr); 1037 ERR(gmsh_err_to_res_T(ierr)); 1038 gmshModelOccGetMass(dim, tag, masses+i, &ierr); 1039 ERR(gmsh_err_to_res_T(ierr)); 1040 d3_add(c, c, d3_muld(tmp, centers+3*i, masses[i])); 1041 m += masses[i]; 1042 } 1043 d3_muld(center, c, 1/m); 1044 } 1045 1046 exit: 1047 if(centers) MEM_RM(allocator, centers); 1048 if(masses) MEM_RM(allocator, masses); 1049 return res; 1050 error: 1051 goto exit; 1052 } 1053 1054 res_T 1055 scad_geometry_get_bounding_box 1056 (struct scad_geometry* geom, 1057 double min[3], 1058 double max[3]) 1059 { 1060 res_T res = RES_OK; 1061 size_t n = 0; 1062 double mi[3], ma[3]; 1063 1064 if(!geom || !min || !max) { 1065 res = RES_BAD_ARG; 1066 goto error; 1067 } 1068 1069 ERR(check_device(FUNC_NAME)); 1070 1071 d3_splat(mi, DBL_MAX); 1072 d3_splat(ma, -DBL_MAX); 1073 for(n = 0; n < geom->gmsh_dimTags_n; n += 2) { 1074 double i[3], a[3]; 1075 int ierr = 0; 1076 int dim = geom->gmsh_dimTags[n]; 1077 int tag = geom->gmsh_dimTags[n + 1]; 1078 gmshModelOccGetBoundingBox(dim, tag, i, i+1, i+2, a, a+1, a+2, &ierr); 1079 ERR(gmsh_err_to_res_T(ierr)); 1080 d3_min(mi, mi, i); 1081 d3_max(ma, ma, a); 1082 } 1083 d3_set(min, mi); 1084 d3_set(max, ma); 1085 1086 exit: 1087 return res; 1088 error: 1089 goto exit; 1090 } 1091 1092 res_T 1093 scad_add_rectangle 1094 (const double xyz[3], 1095 const double dxdy[2], 1096 struct scad_geometry** out_geometry) 1097 { 1098 res_T res = RES_OK; 1099 int ierr, gmsh_ID; 1100 struct scad_geometry* geom = NULL; 1101 struct scad_device* dev = get_device(); 1102 struct mem_allocator* allocator = NULL; 1103 1104 if(!xyz || !dxdy || !out_geometry) { 1105 res = RES_BAD_ARG; 1106 goto error; 1107 } 1108 1109 ERR(check_device(FUNC_NAME)); 1110 allocator = dev->allocator; 1111 1112 gmsh_ID = gmshModelOccAddRectangle(SPLIT3(xyz), SPLIT2(dxdy), -1, 0, &ierr); 1113 ERR(gmsh_err_to_res_T(ierr)); 1114 1115 ERR(geometry_create(&geom)); 1116 geom->gmsh_dimTags_n = 2; 1117 geom->gmsh_dimTags 1118 = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags)); 1119 if(!geom->gmsh_dimTags) { 1120 res = RES_MEM_ERR; 1121 goto error; 1122 } 1123 geom->gmsh_dimTags[0] = 2; 1124 geom->gmsh_dimTags[1] = gmsh_ID; 1125 1126 ERR(device_register_tags(geom)); 1127 1128 exit: 1129 if(out_geometry) *out_geometry = geom; 1130 return res; 1131 error: 1132 if(geom) { 1133 SCAD(geometry_ref_put(geom)); 1134 geom = NULL; 1135 } 1136 goto exit; 1137 } 1138 1139 res_T 1140 scad_add_disk 1141 (const double xyz[3], 1142 const double radius, 1143 struct scad_geometry** out_geometry) 1144 { 1145 res_T res = RES_OK; 1146 int ierr, gmsh_ID; 1147 struct scad_geometry* geom = NULL; 1148 struct scad_device* dev = get_device(); 1149 struct mem_allocator* allocator = NULL; 1150 1151 if(!xyz || radius <= 0 || !out_geometry) { 1152 res = RES_BAD_ARG; 1153 goto error; 1154 } 1155 1156 ERR(check_device(FUNC_NAME)); 1157 allocator = dev->allocator; 1158 1159 gmsh_ID = gmshModelOccAddDisk(SPLIT3(xyz), radius, radius, -1, NULL, 0, 1160 NULL, 0, &ierr); 1161 ERR(gmsh_err_to_res_T(ierr)); 1162 1163 ERR(geometry_create(&geom)); 1164 geom->gmsh_dimTags_n = 2; 1165 geom->gmsh_dimTags 1166 = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags)); 1167 if(!geom->gmsh_dimTags) { 1168 res = RES_MEM_ERR; 1169 goto error; 1170 } 1171 geom->gmsh_dimTags[0] = 2; 1172 geom->gmsh_dimTags[1] = gmsh_ID; 1173 1174 ERR(device_register_tags(geom)); 1175 1176 exit: 1177 if(out_geometry) *out_geometry = geom; 1178 return res; 1179 error: 1180 if(geom) { 1181 SCAD(geometry_ref_put(geom)); 1182 geom = NULL; 1183 } 1184 goto exit; 1185 } 1186 1187 res_T 1188 scad_add_polygon 1189 (void (*get_position)(const size_t ivert, double pos[2], void* data), 1190 void* data, 1191 const double z, 1192 const size_t count, 1193 struct scad_geometry** out_geometry) 1194 { 1195 res_T res = RES_OK; 1196 int ierr, gmsh_ID; 1197 struct scad_geometry* geom = NULL; 1198 size_t i; 1199 int* points = NULL; 1200 int* lines = NULL; 1201 int loop; 1202 struct scad_device* dev = get_device(); 1203 struct mem_allocator* allocator = NULL; 1204 1205 if(!get_position || count < 3 || !out_geometry) { 1206 res = RES_BAD_ARG; 1207 goto error; 1208 } 1209 1210 ERR(check_device(FUNC_NAME)); 1211 allocator = dev->allocator; 1212 1213 points = MEM_ALLOC(allocator, count * sizeof(*points)); 1214 lines = MEM_ALLOC(allocator, count * sizeof(*lines)); 1215 if(!points || !lines) { 1216 res = RES_MEM_ERR; 1217 goto error; 1218 } 1219 1220 for(i=0; i<count; ++i) { 1221 double pos[2]; 1222 get_position(i, pos, data); 1223 points[i] = gmshModelOccAddPoint(pos[0], pos[1], z, -1, -1, &ierr); 1224 ERR(gmsh_err_to_res_T(ierr)); 1225 } 1226 1227 for(i=0; i<count; i++) { 1228 size_t end = (i == count-1) ? 0 : i+1; 1229 lines[i] = gmshModelOccAddLine(points[i], points[end], -1, &ierr); 1230 ERR(gmsh_err_to_res_T(ierr)); 1231 } 1232 1233 loop = gmshModelOccAddCurveLoop(lines, count, -1, &ierr); 1234 ERR(gmsh_err_to_res_T(ierr)); 1235 1236 gmsh_ID = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr); 1237 ERR(gmsh_err_to_res_T(ierr)); 1238 1239 ERR(geometry_create(&geom)); 1240 geom->gmsh_dimTags_n = 2; 1241 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1242 if(!geom->gmsh_dimTags) { 1243 res = RES_MEM_ERR; 1244 goto error; 1245 } 1246 geom->gmsh_dimTags[0] = 2; 1247 geom->gmsh_dimTags[1] = gmsh_ID; 1248 1249 ERR(device_register_tags(geom)); 1250 1251 exit: 1252 if(out_geometry) *out_geometry = geom; 1253 if(allocator) { 1254 MEM_RM(allocator, points); 1255 MEM_RM(allocator, lines); 1256 } 1257 return res; 1258 error: 1259 if(geom) { 1260 SCAD(geometry_ref_put(geom)); 1261 geom = NULL; 1262 } 1263 goto exit; 1264 } 1265 1266 res_T 1267 scad_add_box 1268 (const double xyz[3], 1269 const double dxdydz[3], 1270 struct scad_geometry** out_geometry) 1271 { 1272 res_T res = RES_OK; 1273 int ierr, gmsh_ID; 1274 struct scad_geometry* geom = NULL; 1275 struct scad_device* dev = get_device(); 1276 struct mem_allocator* allocator = NULL; 1277 1278 if(!xyz || !dxdydz || !out_geometry) { 1279 res = RES_BAD_ARG; 1280 goto error; 1281 } 1282 1283 ERR(check_device(FUNC_NAME)); 1284 allocator = dev->allocator; 1285 1286 gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr); 1287 ERR(gmsh_err_to_res_T(ierr)); 1288 1289 ERR(geometry_create(&geom)); 1290 geom->gmsh_dimTags_n = 2; 1291 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1292 if(!geom->gmsh_dimTags) { 1293 res = RES_MEM_ERR; 1294 goto error; 1295 } 1296 geom->gmsh_dimTags[0] = 3; 1297 geom->gmsh_dimTags[1] = gmsh_ID; 1298 1299 ERR(device_register_tags(geom)); 1300 1301 exit: 1302 if(out_geometry) *out_geometry = geom; 1303 return res; 1304 error: 1305 if(geom) { 1306 SCAD(geometry_ref_put(geom)); 1307 geom = NULL; 1308 } 1309 goto exit; 1310 } 1311 1312 res_T 1313 scad_add_cylinder 1314 (const double xyz[3], 1315 const double axis[3], 1316 const double radius, 1317 const double angle, 1318 struct scad_geometry** out_geometry) 1319 { 1320 res_T res = RES_OK; 1321 int ierr, gmsh_ID; 1322 struct scad_geometry* geom = NULL; 1323 struct scad_device* dev = get_device(); 1324 struct mem_allocator* allocator = NULL; 1325 1326 if(!xyz || !axis || radius <= 0 || angle < 0 || angle > 2*PI || !out_geometry) { 1327 res = RES_BAD_ARG; 1328 goto error; 1329 } 1330 1331 ERR(check_device(FUNC_NAME)); 1332 allocator = dev->allocator; 1333 1334 gmsh_ID = gmshModelOccAddCylinder(SPLIT3(xyz), SPLIT3(axis), radius, -1, 1335 angle, &ierr); 1336 ERR(gmsh_err_to_res_T(ierr)); 1337 1338 ERR(geometry_create(&geom)); 1339 geom->gmsh_dimTags_n = 2; 1340 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1341 if(!geom->gmsh_dimTags) { 1342 res = RES_MEM_ERR; 1343 goto error; 1344 } 1345 geom->gmsh_dimTags[0] = 3; 1346 geom->gmsh_dimTags[1] = gmsh_ID; 1347 1348 ERR(device_register_tags(geom)); 1349 1350 exit: 1351 if(out_geometry) *out_geometry = geom; 1352 return res; 1353 error: 1354 if(geom) { 1355 SCAD(geometry_ref_put(geom)); 1356 geom = NULL; 1357 } 1358 goto exit; 1359 } 1360 1361 int 1362 scad_add_sphere 1363 (const double xyz[3], 1364 const double radius, 1365 struct scad_geometry** out_geometry) 1366 { 1367 res_T res = RES_OK; 1368 int ierr, gmsh_ID; 1369 struct scad_geometry* geom = NULL; 1370 struct scad_device* dev = get_device(); 1371 struct mem_allocator* allocator = NULL; 1372 1373 if(!xyz || radius <= 0 || !out_geometry) { 1374 res = RES_BAD_ARG; 1375 goto error; 1376 } 1377 1378 ERR(check_device(FUNC_NAME)); 1379 allocator = dev->allocator; 1380 1381 gmsh_ID = 1382 gmshModelOccAddSphere(SPLIT3(xyz), radius, -1, -PI/2, PI/2, 2*PI, &ierr); 1383 ERR(gmsh_err_to_res_T(ierr)); 1384 1385 ERR(geometry_create(&geom)); 1386 geom->gmsh_dimTags_n = 2; 1387 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1388 if(!geom->gmsh_dimTags) { 1389 res = RES_MEM_ERR; 1390 goto error; 1391 } 1392 geom->gmsh_dimTags[0] = 3; 1393 geom->gmsh_dimTags[1] = gmsh_ID; 1394 1395 ERR(device_register_tags(geom)); 1396 1397 exit: 1398 if(out_geometry) *out_geometry = geom; 1399 return res; 1400 error: 1401 if(geom) { 1402 SCAD(geometry_ref_put(geom)); 1403 geom = NULL; 1404 } 1405 goto exit; 1406 } 1407 1408 SCAD_API res_T 1409 scad_add_cone 1410 (const double center[3], 1411 const double axis[3], 1412 const double radius1, 1413 const double radius2, 1414 const double angle, 1415 struct scad_geometry** cone) 1416 { 1417 res_T res = RES_OK; 1418 int ierr, gmsh_ID; 1419 struct scad_geometry* geom = NULL; 1420 struct scad_device* dev = get_device(); 1421 struct mem_allocator* allocator = NULL; 1422 1423 if(!center || !axis || radius1 < 0 || radius2 < 0 || (radius1 == radius2) 1424 || angle < 0 || angle > 2*PI || !cone) 1425 { 1426 res = RES_BAD_ARG; 1427 goto error; 1428 } 1429 1430 ERR(check_device(FUNC_NAME)); 1431 allocator = dev->allocator; 1432 1433 gmsh_ID = 1434 gmshModelOccAddCone(SPLIT3(center), SPLIT3(axis), radius1, radius2, 1435 -1, 2*PI, &ierr); 1436 ERR(gmsh_err_to_res_T(ierr)); 1437 1438 ERR(geometry_create(&geom)); 1439 geom->gmsh_dimTags_n = 2; 1440 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1441 if(!geom->gmsh_dimTags) { 1442 res = RES_MEM_ERR; 1443 goto error; 1444 } 1445 geom->gmsh_dimTags[0] = 3; 1446 geom->gmsh_dimTags[1] = gmsh_ID; 1447 1448 ERR(device_register_tags(geom)); 1449 1450 exit: 1451 if(cone) *cone = geom; 1452 return res; 1453 error: 1454 if(geom) { 1455 SCAD(geometry_ref_put(geom)); 1456 geom = NULL; 1457 } 1458 goto exit; 1459 } 1460 1461 SCAD_API res_T 1462 scad_add_torus 1463 (const double center[3], 1464 const double radius1, 1465 const double radius2, 1466 const double angle, 1467 const double z_axis[3], /* Can be NULL */ 1468 struct scad_geometry** torus) 1469 { 1470 res_T res = RES_OK; 1471 int ierr, gmsh_ID; 1472 struct scad_geometry* geom = NULL; 1473 struct scad_device* dev = get_device(); 1474 struct mem_allocator* allocator = NULL; 1475 1476 if(!center || radius1 <= 0 || radius2 <= 0 || angle < 0 || angle > 2*PI 1477 || !torus) 1478 { 1479 res = RES_BAD_ARG; 1480 goto error; 1481 } 1482 1483 ERR(check_device(FUNC_NAME)); 1484 allocator = dev->allocator; 1485 1486 gmsh_ID = 1487 gmshModelOccAddTorus(SPLIT3(center), radius1, radius2, 1488 -1, 2*PI, z_axis, (z_axis == NULL ? 0 : 3), &ierr); 1489 ERR(gmsh_err_to_res_T(ierr)); 1490 1491 ERR(geometry_create(&geom)); 1492 geom->gmsh_dimTags_n = 2; 1493 geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); 1494 if(!geom->gmsh_dimTags) { 1495 res = RES_MEM_ERR; 1496 goto error; 1497 } 1498 geom->gmsh_dimTags[0] = 3; 1499 geom->gmsh_dimTags[1] = gmsh_ID; 1500 1501 ERR(device_register_tags(geom)); 1502 1503 exit: 1504 if(torus) *torus = geom; 1505 return res; 1506 error: 1507 if(geom) { 1508 SCAD(geometry_ref_put(geom)); 1509 geom = NULL; 1510 } 1511 goto exit; 1512 } 1513 1514 SCAD_API res_T 1515 scad_geometry_equal 1516 (const struct scad_geometry* geom1, 1517 const struct scad_geometry* geom2, 1518 int* equal) 1519 { 1520 res_T res = RES_OK; 1521 struct scad_device* dev = get_device(); 1522 struct mem_allocator* allocator = NULL; 1523 struct htable_tags t2, t3; 1524 int eq = 1, initialized = 0; 1525 1526 if(!geom1 || !geom2 || !equal) { 1527 res = RES_BAD_ARG; 1528 goto error; 1529 } 1530 1531 ERR(check_device(FUNC_NAME)); 1532 allocator = dev->allocator; 1533 1534 /* Trivial cases */ 1535 if(geom1 == geom2) { 1536 eq = 1; 1537 } 1538 else if(geom1->gmsh_dimTags_n != geom2->gmsh_dimTags_n) { 1539 eq = 0; 1540 } else { 1541 size_t i; 1542 htable_tags_init(allocator, &t2); 1543 htable_tags_init(allocator, &t3); 1544 initialized = 1; 1545 /* Create tables from geom1 tags */ 1546 for(i = 0; i < geom1->gmsh_dimTags_n; i += 2) { 1547 char one = 1; 1548 int d = geom1->gmsh_dimTags[i]; 1549 int t = geom1->gmsh_dimTags[i+1]; 1550 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 1551 ERR(htable_tags_set(tn, &t, &one)); 1552 } 1553 ASSERT((htable_tags_size_get(&t2) + htable_tags_size_get(&t3)) * 2 1554 == geom1->gmsh_dimTags_n); 1555 /* Check if tags from geom2 are included */ 1556 for(i = 0; i < geom2->gmsh_dimTags_n; i += 2) { 1557 char* found; 1558 int d = geom2->gmsh_dimTags[i]; 1559 int t = geom2->gmsh_dimTags[i+1]; 1560 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 1561 found = htable_tags_find(tn, &t); 1562 if(!found) { 1563 eq = 0; 1564 break; 1565 } 1566 } 1567 } 1568 1569 *equal = eq; 1570 1571 exit: 1572 if(initialized) { 1573 htable_tags_release(&t2); 1574 htable_tags_release(&t3); 1575 } 1576 return res; 1577 error: 1578 goto exit; 1579 } 1580 1581 SCAD_API res_T 1582 scad_geometry_is_included 1583 (const struct scad_geometry* geometry, 1584 struct scad_geometry** geometries, 1585 const size_t geometries_count, 1586 int* included) 1587 { 1588 res_T res = RES_OK; 1589 struct scad_device* dev = get_device(); 1590 struct mem_allocator* allocator = NULL; 1591 struct htable_tags t2, t3; 1592 int initialized = 0; 1593 size_t i, n; 1594 1595 if(!geometry || !geometries || geometries_count == 0 || !included) { 1596 res = RES_BAD_ARG; 1597 goto error; 1598 } 1599 1600 ERR(check_device(FUNC_NAME)); 1601 allocator = dev->allocator; 1602 1603 /* Trivial case */ 1604 for(i = 0; i < geometries_count; i++) { 1605 if(geometry == geometries[i]) { 1606 *included = 1; 1607 goto exit; 1608 } 1609 } 1610 1611 /* Create tables from geometries tags */ 1612 htable_tags_init(allocator, &t2); 1613 htable_tags_init(allocator, &t3); 1614 initialized = 1; 1615 for(n = 0; n < geometries_count; n++) { 1616 const struct scad_geometry* geom = geometries[n]; 1617 for(i = 0; i < geometries[n]->gmsh_dimTags_n; i += 2) { 1618 char one = 1; 1619 int d = geom->gmsh_dimTags[i]; 1620 int t = geom->gmsh_dimTags[i+1]; 1621 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 1622 ERR(htable_tags_set(tn, &t, &one)); 1623 } 1624 } 1625 1626 /* Check if tags from geometry are included */ 1627 for(i = 0; i < geometry->gmsh_dimTags_n; i += 2) { 1628 char* found; 1629 int d = geometry->gmsh_dimTags[i]; 1630 int t = geometry->gmsh_dimTags[i+1]; 1631 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 1632 found = htable_tags_find(tn, &t); 1633 if(!found) { 1634 *included = 0; 1635 goto exit; 1636 } 1637 } 1638 1639 /* If here, no not-included tag was found */ 1640 *included = 1; 1641 1642 exit: 1643 if(initialized) { 1644 htable_tags_release(&t2); 1645 htable_tags_release(&t3); 1646 } 1647 return res; 1648 error: 1649 goto exit; 1650 } 1651 1652 res_T 1653 scad_geometries_fuse 1654 (struct scad_geometry** geometries, 1655 const size_t geometries_count, 1656 struct scad_geometry** tools, 1657 const size_t tools_count, 1658 struct scad_geometry** out_geometry) 1659 { 1660 res_T res = RES_OK; 1661 int* tagout = NULL; 1662 int** map = NULL; 1663 size_t* mapn = NULL; 1664 size_t tagoutn, mapnn = 0, sz1, sz2; 1665 int* data1 = NULL; 1666 int* data2 = NULL; 1667 int ierr = 0; 1668 struct scad_geometry* geom = NULL; 1669 struct scad_device* dev = get_device(); 1670 struct mem_allocator* allocator = NULL; 1671 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1672 int *tmp1 = NULL, *tmp2 = NULL; 1673 size_t c1, c2; 1674 #endif 1675 1676 if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { 1677 res = RES_BAD_ARG; 1678 goto error; 1679 } 1680 1681 ERR(check_device(FUNC_NAME)); 1682 allocator = dev->allocator; 1683 1684 ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); 1685 ERR(gather_tags(tools, tools_count, &data2, &sz2)); 1686 1687 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1688 gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); 1689 ERR(gmsh_err_to_res_T(ierr)); 1690 gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); 1691 ERR(gmsh_err_to_res_T(ierr)); 1692 /* We do remove gmsh objects here as they are temporary copies created to 1693 * allow the remove flag to be used (gmsh is broken here if remove=0) */ 1694 gmshModelOccFuse(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn, &mapnn, 1695 -1, 1, 1, &ierr); 1696 #else 1697 /* We don't remove gmsh objects here; they are only removed when their tags are 1698 * no longer used by any star-cad geometry */ 1699 gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, 1700 &mapnn, -1, 0, 0, &ierr); 1701 #endif 1702 1703 ASSERT(tagoutn % 2 == 0); 1704 ERR(gmsh_err_to_res_T(ierr)); 1705 1706 ERR(geometry_create(&geom)); 1707 geom->gmsh_dimTags_n = tagoutn; 1708 geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 1709 if(!geom->gmsh_dimTags) { 1710 res = RES_MEM_ERR; 1711 goto error; 1712 } 1713 memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 1714 1715 ERR(device_register_tags(geom)); 1716 1717 exit: 1718 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1719 gmshFree(tmp1); 1720 gmshFree(tmp2); 1721 #endif 1722 if(out_geometry) *out_geometry = geom; 1723 if(allocator) { 1724 MEM_RM(allocator, data1); 1725 MEM_RM(allocator, data2); 1726 } 1727 gmshFree(mapn); 1728 gmshFree(tagout); 1729 free_gmsh_map(map, mapnn); 1730 return res; 1731 error: 1732 if(geom) { 1733 SCAD(geometry_ref_put(geom)); 1734 geom = NULL; 1735 } 1736 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 1737 goto exit; 1738 } 1739 1740 res_T 1741 scad_geometries_collect 1742 (struct scad_geometry** geometries, 1743 const size_t geometries_count, 1744 struct scad_geometry** out_geometry) 1745 { 1746 res_T res = RES_OK; 1747 size_t sz; 1748 int* data = NULL; 1749 struct scad_geometry* geom = NULL; 1750 1751 if(!geometries || !geometries_count || !out_geometry) { 1752 res = RES_BAD_ARG; 1753 goto error; 1754 } 1755 1756 ERR(check_device(FUNC_NAME)); 1757 1758 ERR(gather_tags(geometries, geometries_count, &data, &sz)); 1759 1760 ERR(geometry_create(&geom)); 1761 geom->gmsh_dimTags_n = sz; 1762 geom->gmsh_dimTags = data; 1763 1764 ERR(device_register_tags(geom)); 1765 1766 exit: 1767 if(out_geometry) *out_geometry = geom; 1768 return res; 1769 error: 1770 if(geom) { 1771 SCAD(geometry_ref_put(geom)); 1772 geom = NULL; 1773 } 1774 goto exit; 1775 } 1776 1777 res_T 1778 scad_geometries_cut 1779 (struct scad_geometry** geometries, 1780 const size_t geometries_count, 1781 struct scad_geometry** tools, 1782 const size_t tools_count, 1783 struct scad_geometry** out_geometry) 1784 { 1785 res_T res = RES_OK; 1786 int* tagout = NULL; 1787 int** map = NULL; 1788 size_t* mapn = NULL; 1789 size_t tagoutn, mapnn = 0, sz1, sz2; 1790 int* data1 = NULL; 1791 int* data2 = NULL; 1792 int ierr = 0; 1793 struct scad_geometry* geom = NULL; 1794 struct scad_device* dev = get_device(); 1795 struct mem_allocator* allocator = NULL; 1796 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1797 int *tmp1 = NULL, *tmp2 = NULL; 1798 size_t c1, c2; 1799 #endif 1800 1801 if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { 1802 res = RES_BAD_ARG; 1803 goto error; 1804 } 1805 1806 ERR(check_device(FUNC_NAME)); 1807 allocator = dev->allocator; 1808 1809 ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); 1810 ERR(gather_tags(tools, tools_count, &data2, &sz2)); 1811 1812 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1813 gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); 1814 ERR(gmsh_err_to_res_T(ierr)); 1815 gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); 1816 ERR(gmsh_err_to_res_T(ierr)); 1817 /* We do remove gmsh objects here as they are temporary copies created to 1818 * allow the remove flag to be used (gmsh is broken here if remove=0) */ 1819 gmshModelOccCut(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn, 1820 &mapnn, -1, 1, 1, &ierr); 1821 #else 1822 /* We don't remove gmsh objects here; they are only removed when their tags are 1823 * no longer used by any star-cad geometry */ 1824 gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, 1825 &mapnn, -1, 0, 0, &ierr); 1826 #endif 1827 1828 ASSERT(tagoutn % 2 == 0); 1829 ERR(gmsh_err_to_res_T(ierr)); 1830 1831 ERR(geometry_create(&geom)); 1832 geom->gmsh_dimTags_n = tagoutn; 1833 geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 1834 if(!geom->gmsh_dimTags) { 1835 res = RES_MEM_ERR; 1836 goto error; 1837 } 1838 memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 1839 1840 ERR(device_register_tags(geom)); 1841 1842 exit: 1843 if(out_geometry) *out_geometry = geom; 1844 if(allocator) { 1845 MEM_RM(allocator, data1); 1846 MEM_RM(allocator, data2); 1847 } 1848 gmshFree(mapn); 1849 gmshFree(tagout); 1850 free_gmsh_map(map, mapnn); 1851 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1852 gmshFree(tmp1); 1853 gmshFree(tmp2); 1854 #endif 1855 return res; 1856 error: 1857 if(ierr) { 1858 int dim = INT_MAX; 1859 if(!mixed_dim_err_msg("cut", geometries, geometries_count, &dim)) 1860 mixed_dim_err_msg("cut", tools, tools_count, &dim); 1861 if(geom) { 1862 SCAD(geometry_ref_put(geom)); 1863 geom = NULL; 1864 } 1865 } 1866 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 1867 goto exit; 1868 } 1869 1870 res_T 1871 scad_geometries_intersect 1872 (struct scad_geometry** geometries, 1873 const size_t geometries_count, 1874 struct scad_geometry** tools, 1875 const size_t tools_count, 1876 struct scad_geometry** out_geometry) 1877 { 1878 res_T res = RES_OK; 1879 int* tagout = NULL; 1880 int** map = NULL; 1881 size_t* mapn = NULL; 1882 size_t tagoutn, mapnn = 0, sz1, sz2; 1883 int* data1 = NULL; 1884 int* data2 = NULL; 1885 int ierr = 0; 1886 struct scad_geometry* geom = NULL; 1887 struct scad_device* dev = get_device(); 1888 struct mem_allocator* allocator = NULL; 1889 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1890 int *tmp1 = NULL, *tmp2 = NULL; 1891 size_t c1, c2; 1892 #endif 1893 1894 if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { 1895 res = RES_BAD_ARG; 1896 goto error; 1897 } 1898 1899 ERR(check_device(FUNC_NAME)); 1900 allocator = dev->allocator; 1901 1902 ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); 1903 ERR(gather_tags(tools, tools_count, &data2, &sz2)); 1904 1905 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1906 gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); 1907 ERR(gmsh_err_to_res_T(ierr)); 1908 gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); 1909 ERR(gmsh_err_to_res_T(ierr)); 1910 /* We do remove gmsh objects here as they are temporary copies created to 1911 * allow the remove flag to be used (gmsh is broken here if remove=0) */ 1912 gmshModelOccIntersect(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, 1913 &mapn, &mapnn, -1, 1, 1, &ierr); 1914 #else 1915 /* We don't remove gmsh objects here; they are only removed when their tags are 1916 * no longer used by any star-cad geometry */ 1917 gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, 1918 &mapnn, -1, 0, 0, &ierr); 1919 #endif 1920 1921 ASSERT(tagoutn % 2 == 0); 1922 ERR(gmsh_err_to_res_T(ierr)); 1923 1924 ERR(geometry_create(&geom)); 1925 geom->gmsh_dimTags_n = tagoutn; 1926 if(tagoutn != 0){ 1927 geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 1928 if(!geom->gmsh_dimTags) { 1929 res = RES_MEM_ERR; 1930 goto error; 1931 } 1932 memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 1933 } 1934 1935 ERR(device_register_tags(geom)); 1936 1937 exit: 1938 if(out_geometry) *out_geometry = geom; 1939 if(allocator) { 1940 MEM_RM(allocator, data1); 1941 MEM_RM(allocator, data2); 1942 } 1943 gmshFree(mapn); 1944 gmshFree(tagout); 1945 free_gmsh_map(map, mapnn); 1946 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS 1947 gmshFree(tmp1); 1948 gmshFree(tmp2); 1949 #endif 1950 return res; 1951 error: 1952 if(geom) { 1953 SCAD(geometry_ref_put(geom)); 1954 geom = NULL; 1955 } 1956 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 1957 goto exit; 1958 } 1959 1960 res_T 1961 scad_geometries_common_boundaries 1962 (struct scad_geometry** geometries, 1963 const size_t geometries_count, 1964 struct scad_geometry** tools, 1965 const size_t tools_count, 1966 struct scad_geometry*** out_boundaries, 1967 size_t *out_count) 1968 { 1969 res_T res = RES_OK; 1970 int* tagout = NULL; 1971 size_t tagoutn, sz1, sz2, u_sz; 1972 int* data1 = NULL; 1973 int* data2 = NULL; 1974 int* unique = NULL; 1975 int ierr = 0; 1976 int* bound1 = NULL; 1977 int* bound2 = NULL; 1978 size_t n1, n2; 1979 struct scad_geometry** out_geom = NULL; 1980 struct mem_allocator* allocator = NULL; 1981 struct scad_device* dev = get_device(); 1982 int log; 1983 enum log_type log_type; 1984 size_t i, c = 0, n; 1985 struct str msg; 1986 int msg_initialized = 0; 1987 1988 if(!geometries || !geometries_count || !tools || !tools_count 1989 || !out_boundaries || !out_count) 1990 { 1991 res = RES_BAD_ARG; 1992 goto error; 1993 } 1994 1995 ERR(check_device(FUNC_NAME)); 1996 allocator = dev->allocator; 1997 1998 ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); 1999 ERR(gather_tags(tools, tools_count, &data2, &sz2)); 2000 2001 /* data1 and data2 can have tags in common: deduplicate them! 2002 * (even if the refcounting stuff can manage duplicates) */ 2003 ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz)); 2004 2005 ERR(sync_device()); 2006 gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr); 2007 ERR(gmsh_err_to_res_T(ierr)); 2008 gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr); 2009 ERR(gmsh_err_to_res_T(ierr)); 2010 2011 ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn)); 2012 ASSERT(tagoutn % 2 == 0); 2013 c = tagoutn / 2; 2014 2015 if(tagoutn == 0) { 2016 log_message(dev, "Common boundaries list is empty.\n"); 2017 goto exit; 2018 } 2019 2020 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 2021 log_type = dev->log_type; 2022 if(log) { 2023 str_init(allocator, &msg); 2024 msg_initialized = 1; 2025 logger_print(dev->logger, log_type, 2026 "Common boundaries specific tag management:\n"); 2027 ERR(str_printf(&msg, " tags [")); 2028 for(i = 0; i < tagoutn; i += 2) { 2029 const int dim = tagout[i]; 2030 const int tag = tagout[i+1]; 2031 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2032 } 2033 ERR(str_append_printf(&msg, "] getting a ref to tags [")); 2034 for(i = 0; i < u_sz; i += 2) { 2035 const int dim = unique[i]; 2036 const int tag = unique[i+1]; 2037 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2038 } 2039 logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); 2040 } 2041 2042 out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom)); 2043 if(!out_geom) { 2044 res = RES_MEM_ERR; 2045 goto error; 2046 } 2047 2048 for(i = 0, n = 0; i < tagoutn; i += 2, n++) { 2049 int dim = tagout[i]; 2050 int tag = tagout[i+1]; 2051 struct tag_desc* desc; 2052 ASSERT(dim == 2); 2053 ERR(geometry_create(out_geom+n)); 2054 out_geom[n]->gmsh_dimTags_n = 2; 2055 out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator, 2056 2 * sizeof(*out_geom[n]->gmsh_dimTags)); 2057 if(!out_geom[n]->gmsh_dimTags) { 2058 res = RES_MEM_ERR; 2059 goto error; 2060 } 2061 out_geom[n]->gmsh_dimTags[0] = dim; 2062 out_geom[n]->gmsh_dimTags[1] = tag; 2063 ERR(device_register_tags(out_geom[n])); 2064 /* Need to protect out_geometry's tags by getting a ref on input tags or 2065 * deleting input geometry will possibly delete them */ 2066 ERR(device_register_ref_to_tags(dim, tag, unique, u_sz)); 2067 /* As the 2D tags will be deleted when the 3D tag they are part of are 2068 * deleted, they shouldn't be deleted when the geometry they belongs to are 2069 * released. */ 2070 desc = device_get_description(dim, tag); 2071 ASSERT(desc); 2072 desc->delete_policy = Scad_do_not_delete; 2073 } 2074 2075 exit: 2076 if(msg_initialized) str_release(&msg); 2077 if(out_boundaries) *out_boundaries = out_geom; 2078 if(out_count) *out_count = c; 2079 if(allocator) { 2080 MEM_RM(allocator, data1); 2081 MEM_RM(allocator, data2); 2082 MEM_RM(allocator, unique); 2083 MEM_RM(allocator, tagout); 2084 } 2085 gmshFree(bound1); 2086 gmshFree(bound2); 2087 return res; 2088 error: 2089 if(ierr) { 2090 int dim = INT_MAX; 2091 if(!mixed_dim_err_msg("common boundaries", geometries, geometries_count, &dim)) 2092 mixed_dim_err_msg("common boundaries", tools, tools_count, &dim); 2093 } 2094 if(out_geom) { 2095 for(i = 0; i < c; i++) { 2096 if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i])); 2097 } 2098 MEM_RM(allocator, out_geom); 2099 out_geom = NULL; 2100 c = 0; 2101 } 2102 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 2103 goto exit; 2104 } 2105 2106 res_T 2107 scad_geometries_common_boundary 2108 (struct scad_geometry** geometries, 2109 const size_t geometries_count, 2110 struct scad_geometry** tools, 2111 const size_t tools_count, 2112 struct scad_geometry** out_boundary) 2113 { 2114 res_T res = RES_OK; 2115 int* tagout = NULL; 2116 size_t tagoutn, sz1, sz2, u_sz; 2117 int* data1 = NULL; 2118 int* data2 = NULL; 2119 int* unique = NULL; 2120 int ierr = 0; 2121 int* bound1 = NULL; 2122 int* bound2 = NULL; 2123 size_t n1, n2; 2124 struct scad_geometry* out_geom = NULL; 2125 struct mem_allocator* allocator = NULL; 2126 struct scad_device* dev = get_device(); 2127 int log; 2128 enum log_type log_type; 2129 size_t i, n; 2130 struct str msg; 2131 int msg_initialized = 0; 2132 2133 if(!geometries || !geometries_count || !tools || !tools_count || !out_boundary) { 2134 res = RES_BAD_ARG; 2135 goto error; 2136 } 2137 2138 ERR(check_device(FUNC_NAME)); 2139 allocator = dev->allocator; 2140 2141 ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); 2142 ERR(gather_tags(tools, tools_count, &data2, &sz2)); 2143 2144 /* data1 and data2 can have tags in common: deduplicate them! 2145 * (even if the refcounting stuff can manage duplicates) */ 2146 ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz)); 2147 2148 ERR(sync_device()); 2149 gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr); 2150 ERR(gmsh_err_to_res_T(ierr)); 2151 gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr); 2152 ERR(gmsh_err_to_res_T(ierr)); 2153 2154 ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn)); 2155 ASSERT(tagoutn % 2 == 0); 2156 2157 if(tagoutn == 0) { 2158 log_message(dev, "Common boundary list is empty.\n"); 2159 } else { 2160 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 2161 log_type = dev->log_type; 2162 if(log) { 2163 str_init(allocator, &msg); 2164 msg_initialized = 1; 2165 logger_print(dev->logger, log_type, 2166 "Common boundary specific tag management:\n"); 2167 ERR(str_printf(&msg, " tags [")); 2168 for(i = 0; i < tagoutn; i += 2) { 2169 const int dim = tagout[i]; 2170 const int tag = tagout[i+1]; 2171 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2172 } 2173 ERR(str_append_printf(&msg, "] getting a ref to tags [")); 2174 for(i = 0; i < u_sz; i += 2) { 2175 const int dim = unique[i]; 2176 const int tag = unique[i+1]; 2177 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2178 } 2179 logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); 2180 } 2181 } 2182 2183 ERR(geometry_create(&out_geom)); 2184 out_geom->gmsh_dimTags_n = tagoutn; 2185 out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 2186 if(!out_geom->gmsh_dimTags) { 2187 res = RES_MEM_ERR; 2188 goto error; 2189 } 2190 memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 2191 ERR(device_register_tags(out_geom)); 2192 2193 for(i = 0, n = 0; i < tagoutn; i += 2, n++) { 2194 int dim = tagout[i]; 2195 int tag = tagout[i+1]; 2196 struct tag_desc* desc = device_get_description(dim, tag); 2197 ASSERT(desc); 2198 ASSERT(dim == 2); 2199 /* Need to protect out_geometry's tags by getting a ref on input tags or 2200 * deleting input geometry will possibly delete them */ 2201 ERR(device_register_ref_to_tags(dim, tag, unique, u_sz)); 2202 /* As the 2D tags will be deleted when the 3D tag they are part of are 2203 * deleted, they shouldn't be deleted when the geometry they belongs to are 2204 * released. */ 2205 desc->delete_policy = Scad_do_not_delete; 2206 } 2207 2208 exit: 2209 if(msg_initialized) str_release(&msg); 2210 if(out_boundary) *out_boundary = out_geom; 2211 if(allocator) { 2212 MEM_RM(allocator, data1); 2213 MEM_RM(allocator, data2); 2214 MEM_RM(allocator, unique); 2215 MEM_RM(allocator, tagout); 2216 } 2217 gmshFree(bound1); 2218 gmshFree(bound2); 2219 return res; 2220 error: 2221 if(ierr) { 2222 int dim = INT_MAX; 2223 if(!mixed_dim_err_msg("common boundary", geometries, geometries_count, &dim)) 2224 mixed_dim_err_msg("common boundary", tools, tools_count, &dim); 2225 } 2226 if(out_geom) { 2227 SCAD(geometry_ref_put(out_geom)); 2228 out_geom = NULL; 2229 } 2230 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 2231 goto exit; 2232 } 2233 2234 res_T 2235 scad_geometry_rotate 2236 (const struct scad_geometry* geom, 2237 const double pt[3], 2238 const double axis[3], 2239 const double angle, 2240 struct scad_geometry** out_geometry) 2241 { 2242 int ierr = 0; 2243 struct scad_geometry* out = NULL; 2244 res_T res = RES_OK; 2245 2246 if(!geom || !pt || !axis || ! out_geometry) { 2247 res = RES_BAD_ARG; 2248 goto error; 2249 } 2250 2251 ERR(check_device(FUNC_NAME)); 2252 2253 ERR(scad_geometry_copy(geom, &out)); 2254 2255 gmshModelOccRotate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(pt), 2256 SPLIT3(axis), angle, &ierr); 2257 ERR(gmsh_err_to_res_T(ierr)); 2258 2259 exit: 2260 if(out_geometry) *out_geometry = out; 2261 return res; 2262 error: 2263 if(out) SCAD(geometry_ref_put(out)); 2264 out = NULL; 2265 goto exit; 2266 } 2267 2268 res_T 2269 scad_geometry_extrude 2270 (const struct scad_geometry* geom, 2271 const double dxdydz[3], 2272 struct scad_geometry** out_geometry) 2273 { 2274 res_T res = RES_OK; 2275 int *tagout = NULL; 2276 size_t tagoutn; 2277 size_t i; 2278 int *ed, *extrude_data = NULL; 2279 size_t extrude_sz = 0; 2280 int ierr = 0; 2281 struct scad_geometry* extrude_geom = NULL; 2282 struct scad_device* dev = get_device(); 2283 int log; 2284 enum log_type log_type; 2285 struct mem_allocator* allocator = NULL; 2286 struct str msg; 2287 int init = 0; 2288 2289 if(!geom || !dxdydz || !out_geometry) { 2290 res = RES_BAD_ARG; 2291 goto error; 2292 } 2293 2294 ERR(check_device(FUNC_NAME)); 2295 allocator = dev->allocator; 2296 2297 /* OCC cannot extrude 3D geometries */ 2298 for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { 2299 const int dim = geom->gmsh_dimTags[i]; 2300 if(dim != 2) { 2301 if(str_is_empty(&geom->name)) { 2302 log_error(get_device(), 2303 "Cannot extrude unnamed 3D geometry '%p'.\n", 2304 (void*)geom); 2305 } else { 2306 log_error(get_device(), 2307 "Cannot extrude 3D geometry '%s'.\n", 2308 str_cget(&geom->name)); 2309 } 2310 res = RES_BAD_ARG; 2311 goto error; 2312 } 2313 } 2314 2315 gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz), 2316 &tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr); 2317 ERR(gmsh_err_to_res_T(ierr)); 2318 2319 /* Output includes both the 3D result and its 2D constituents. 2320 * Keep only 3D entities. */ 2321 for(i = 0; i < tagoutn; i += 2) { 2322 int dim = tagout[i]; 2323 if(dim == 3) extrude_sz += 2; 2324 } 2325 extrude_data = MEM_ALLOC(allocator, extrude_sz * sizeof(*extrude_data)); 2326 if(!extrude_data) { 2327 res = RES_MEM_ERR; 2328 goto error; 2329 } 2330 ed = extrude_data; 2331 for(i = 0; i < tagoutn; i += 2) { 2332 int dim = tagout[i]; 2333 int tag = tagout[i+1]; 2334 if(dim == 3) { 2335 *ed++ = dim; 2336 *ed++ = tag; 2337 } 2338 } 2339 2340 ERR(geometry_create(&extrude_geom)); 2341 extrude_geom->gmsh_dimTags_n = extrude_sz; 2342 extrude_geom->gmsh_dimTags = extrude_data; 2343 2344 ERR(device_register_tags(extrude_geom)); 2345 2346 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 2347 log_type = dev->log_type; 2348 if(log) { 2349 str_init(allocator, &msg); 2350 init = 1; 2351 logger_print(dev->logger, log_type, "Extrude specific tag management:\n"); 2352 ERR(str_printf(&msg, " tags [")); 2353 for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { 2354 const int dim = geom->gmsh_dimTags[i]; 2355 const int tag = geom->gmsh_dimTags[i+1]; 2356 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2357 } 2358 ERR(str_append_printf(&msg, "] getting a ref to tags [")); 2359 for(i = 0; i < extrude_sz; i += 2) { 2360 const int dim = extrude_data[i]; 2361 const int tag = extrude_data[i+1]; 2362 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 2363 } 2364 logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); 2365 } 2366 for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { 2367 const int dim = geom->gmsh_dimTags[i]; 2368 const int tag = geom->gmsh_dimTags[i+1]; 2369 struct tag_desc* desc = device_get_description(dim, tag); 2370 ASSERT(dim == 2); 2371 ASSERT(desc != NULL); 2372 /* Need to protect input geometry's tags by getting a ref on output tags or 2373 * deleting out_geometry will possibly delete them */ 2374 ERR(device_register_ref_to_tags(dim, tag, extrude_data, extrude_sz)); 2375 /* As the 2D tags will be deleted when the 3D tag they are part of are 2376 * deleted, they shouldn't be deleted when the geometry they belongs to is 2377 * released. */ 2378 desc->delete_policy = Scad_do_not_delete; 2379 } 2380 2381 exit: 2382 if(init) str_release(&msg); 2383 if(out_geometry) *out_geometry = extrude_geom; 2384 gmshFree(tagout); 2385 return res; 2386 error: 2387 if(extrude_geom) { 2388 SCAD(geometry_ref_put(extrude_geom)); 2389 extrude_geom = NULL; 2390 } 2391 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 2392 goto exit; 2393 } 2394 2395 res_T 2396 scad_geometry_explode 2397 (const struct scad_geometry* geom, 2398 struct scad_geometry*** out_geometry, 2399 size_t* out_n) 2400 { 2401 res_T res = RES_OK; 2402 int* data = NULL; 2403 size_t i, n, sz = 0; 2404 struct scad_geometry** geom_array = NULL; 2405 struct str name; 2406 int name_initialized = 0; 2407 struct scad_device* dev = get_device(); 2408 struct mem_allocator* allocator = NULL; 2409 2410 if(!geom || !out_geometry || !out_n) { 2411 res = RES_BAD_ARG; 2412 goto error; 2413 } 2414 2415 ERR(check_device(FUNC_NAME)); 2416 2417 allocator = dev->allocator; 2418 data = geom->gmsh_dimTags; 2419 sz = geom->gmsh_dimTags_n; 2420 2421 ASSERT(sz % 2 == 0); 2422 geom_array = MEM_CALLOC(allocator, sz/2, sizeof(*geom_array)); 2423 if(!geom_array) { 2424 res = RES_MEM_ERR; 2425 goto error; 2426 } 2427 2428 for(i = 0, n = 0; i < sz; i += 2, n++) { 2429 const int dim = data[i]; 2430 const int tag = data[i+1]; 2431 ERR(geometry_create(geom_array+n)); 2432 geom_array[n]->gmsh_dimTags_n = 2; 2433 geom_array[n]->gmsh_dimTags 2434 = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[0]->gmsh_dimTags)); 2435 if(!geom_array[n]->gmsh_dimTags) { 2436 res = RES_MEM_ERR; 2437 goto error; 2438 } 2439 geom_array[n]->gmsh_dimTags[0] = dim; 2440 geom_array[n]->gmsh_dimTags[1] = tag; 2441 2442 ERR(device_register_tags(geom_array[n])); 2443 } 2444 2445 exit: 2446 if(out_n) *out_n = sz/2 ; 2447 if(out_geometry) *out_geometry = geom_array; 2448 if(name_initialized) str_release(&name); 2449 return res; 2450 error: 2451 if(geom_array) { 2452 for(i = 0, n = 0; i < sz; i += 2, n++) { 2453 if(geom_array[n]) SCAD(geometry_ref_put(geom_array[n])); 2454 } 2455 MEM_RM(allocator, geom_array); 2456 geom_array = NULL; 2457 } 2458 goto exit; 2459 } 2460 2461 2462 res_T 2463 scad_geometry_copy 2464 (const struct scad_geometry* geom, 2465 struct scad_geometry** out_geometry) 2466 { 2467 res_T res = RES_OK; 2468 int* data1; 2469 int* tagout = NULL; 2470 size_t sz1, tagoutn; 2471 int ierr = 0; 2472 struct scad_geometry* copy = NULL; 2473 struct scad_device* dev = get_device(); 2474 struct mem_allocator* allocator = NULL; 2475 2476 if(!geom || !out_geometry) { 2477 res = RES_BAD_ARG; 2478 goto error; 2479 } 2480 2481 ERR(check_device(FUNC_NAME)); 2482 allocator = dev->allocator; 2483 2484 sz1 = geom->gmsh_dimTags_n; 2485 data1 = geom->gmsh_dimTags; 2486 gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr); 2487 2488 ASSERT(tagoutn % 2 == 0); 2489 ERR(gmsh_err_to_res_T(ierr)); 2490 2491 ERR(geometry_create(©)); 2492 copy->gmsh_dimTags_n = tagoutn; 2493 copy->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 2494 if(!copy->gmsh_dimTags) { 2495 res = RES_MEM_ERR; 2496 goto error; 2497 } 2498 memcpy(copy->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 2499 2500 ERR(device_register_tags(copy)); 2501 2502 exit: 2503 if(out_geometry) *out_geometry = copy; 2504 gmshFree(tagout); 2505 return res; 2506 error: 2507 if(copy) { 2508 SCAD(geometry_ref_put(copy)); 2509 copy = NULL; 2510 } 2511 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 2512 goto exit; 2513 } 2514 2515 res_T 2516 scad_geometries_set_name 2517 (struct scad_geometry** geometries, 2518 const size_t geometries_count, 2519 const char* prefix_name, /* Can be NULL */ 2520 const size_t from_rank) /* Can be NULL */ 2521 { 2522 res_T res = RES_OK; 2523 struct str tmp; 2524 int initialized = 0; 2525 struct mem_allocator* allocator = NULL; 2526 size_t i, cpt; 2527 2528 if(!geometries || geometries_count == 0) { 2529 res = RES_BAD_ARG; 2530 goto error; 2531 } 2532 2533 ERR(check_device(FUNC_NAME)); 2534 2535 if(!prefix_name) { 2536 for(i = 0; i < geometries_count; i++) { 2537 ERR(geom_set_name(geometries[i], NULL)); 2538 } 2539 } 2540 else if(strlen(prefix_name) == 0) { 2541 log_error(get_device(), "Geometry name \"\" is invalid.\n"); 2542 res = RES_BAD_ARG; 2543 goto error; 2544 } else { 2545 str_init(allocator, &tmp); 2546 initialized = 1; 2547 cpt = from_rank; 2548 for(i = 0; i < geometries_count; i++) { 2549 ERR(str_printf(&tmp, "%s_%ld", prefix_name, cpt++)); 2550 ERR(geom_set_name(geometries[i], &tmp)); 2551 } 2552 } 2553 2554 exit: 2555 if(initialized) str_release(&tmp); 2556 return res; 2557 error: 2558 goto exit; 2559 } 2560 2561 res_T 2562 scad_geometry_set_name 2563 (struct scad_geometry* geom, 2564 const char* name) /* Can be NULL */ 2565 { 2566 res_T res = RES_OK; 2567 struct str tmp; 2568 int initialized = 0; 2569 struct mem_allocator* allocator = NULL; 2570 2571 if(!geom) { 2572 res = RES_BAD_ARG; 2573 goto error; 2574 } 2575 2576 ERR(check_device(FUNC_NAME)); 2577 2578 if(!name) { 2579 ERR(geom_set_name(geom, NULL)); 2580 } 2581 else if(strlen(name) == 0) { 2582 log_error(get_device(), "Geometry name \"\" is invalid.\n"); 2583 res = RES_BAD_ARG; 2584 goto error; 2585 } else { 2586 str_init(allocator, &tmp); 2587 initialized = 1; 2588 ERR(str_set(&tmp, name)); 2589 ERR(geom_set_name(geom, &tmp)); 2590 } 2591 2592 exit: 2593 if(initialized) str_release(&tmp); 2594 return res; 2595 error: 2596 goto exit; 2597 } 2598 2599 res_T 2600 scad_geometry_translate 2601 (const struct scad_geometry* geom, 2602 const double dxdydz[3], 2603 struct scad_geometry** out_geometry) 2604 { 2605 int ierr = 0; 2606 struct scad_geometry* out = NULL; 2607 res_T res = RES_OK; 2608 2609 if(!geom || !dxdydz || ! out_geometry) { 2610 res = RES_BAD_ARG; 2611 goto error; 2612 } 2613 2614 ERR(check_device(FUNC_NAME)); 2615 2616 ERR(scad_geometry_copy(geom, &out)); 2617 2618 gmshModelOccTranslate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(dxdydz), 2619 &ierr); 2620 ERR(gmsh_err_to_res_T(ierr)); 2621 2622 exit: 2623 if(out_geometry) *out_geometry = out; 2624 return res; 2625 error: 2626 if(out) SCAD(geometry_ref_put(out)); 2627 out = NULL; 2628 goto exit; 2629 } 2630 2631 static char 2632 geom_uses_dimTag 2633 (const struct scad_geometry* g, 2634 const int dim, 2635 const int tag) 2636 { 2637 size_t i; 2638 ASSERT(g); 2639 for(i = 0; i < g->gmsh_dimTags_n; i+=2) { 2640 int d = g->gmsh_dimTags[i]; 2641 int t = g->gmsh_dimTags[i+1]; 2642 if(dim == d && tag == t) return 1; 2643 } 2644 return 0; 2645 } 2646 2647 res_T 2648 scad_geometries_partition 2649 (struct scad_geometry** geometries, 2650 const size_t geometries_count, 2651 const int flags, 2652 struct scad_geometry** out_geometries) 2653 { 2654 res_T res = RES_OK; 2655 size_t i; 2656 int* tagout = NULL; 2657 int** map = NULL; 2658 size_t* mapn = NULL; 2659 size_t tagoutn = 0, mapnn = 0, sz; 2660 int* data = NULL; 2661 int ierr = 0; 2662 struct scad_geometry** geoms = NULL; 2663 struct htable_mappings m2, m3; 2664 struct scad_device* dev = get_device(); 2665 struct htable_tags t2, t3; 2666 struct htable_tags_iterator it, end; 2667 int ht_initialized = 0, tmp_initialized = 0; 2668 struct mem_allocator* allocator = NULL; 2669 int dont_call_fragment = 0; 2670 char* overlap = NULL; 2671 const int invalid_flags = 2672 ~(SCAD_ALLOW_OVERLAPPING | SCAD_DUMP_ON_OVERLAPPING_ERROR); 2673 const int dump_overlapping_err = flags & SCAD_DUMP_ON_OVERLAPPING_ERROR; 2674 static size_t err_cpt = 0; 2675 struct str tmp; 2676 2677 if(!geometries || !geometries_count || !out_geometries || flags & invalid_flags) { 2678 res = RES_BAD_ARG; 2679 goto error; 2680 } 2681 2682 ERR(check_device(FUNC_NAME)); 2683 allocator = dev->allocator; 2684 str_init(allocator, &tmp); 2685 tmp_initialized = 1; 2686 2687 ERR(gather_tags(geometries, geometries_count, &data, &sz)); 2688 2689 /* Create output geometries */ 2690 geoms = MEM_CALLOC(allocator, geometries_count, sizeof(*geoms)); 2691 if(!geoms) { 2692 res = RES_MEM_ERR; 2693 goto error; 2694 } 2695 2696 dont_call_fragment = (sz == 2); 2697 if(dont_call_fragment) { 2698 /* gmshModelOccFragment doesn't allow to process a single entity: need to 2699 * create the result (same as input) here. 2700 * Not so simple as one could have provided more than 1 input geometry with 2701 * identical or no dim.tag(s). */ 2702 mapnn = 1; 2703 map = gmshMalloc(sizeof(*map)); 2704 map[0] = gmshMalloc(2 * sizeof(**map)); 2705 mapn = gmshMalloc(sizeof(*mapn)); 2706 if(!map || !map[0] || !mapn) { 2707 res = RES_MEM_ERR; 2708 goto error; 2709 } 2710 mapn[0] = 2; 2711 map[0][0] = data[0]; 2712 map[0][1] = data[1]; 2713 } else { 2714 /* As a general principle, we don't remove gmsh objects directly; they are 2715 * only removed from scad_geometry_ref_put when their tags are no longer 2716 * used by any star-cad geometry. */ 2717 gmshModelOccFragment(data, sz, NULL, 0, &tagout, &tagoutn, &map, &mapn, 2718 &mapnn, -1, 0, 0, &ierr); 2719 ERR(gmsh_err_to_res_T(ierr)); 2720 ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */ 2721 2722 /* Check first if there was an overlapping problem */ 2723 if(!(flags & SCAD_ALLOW_OVERLAPPING)) { 2724 /* No overlapping means that each tag in geometries is translated into a 2725 * single tag in map */ 2726 size_t ov = 0; 2727 overlap = MEM_CALLOC(allocator, geometries_count, sizeof(*overlap)); 2728 if(!overlap) { 2729 res = RES_MEM_ERR; 2730 goto error; 2731 } 2732 for(i = 0; i < mapnn; i++) { 2733 if(mapn[i] != 2) { 2734 int dim = data[2*i]; 2735 int tag = data[2*i+1]; 2736 size_t k; 2737 ov++; 2738 res = RES_BAD_ARG; 2739 /* dim.tag #i overlaps: search geometries using it in input */ 2740 for(k = 0; k < geometries_count; k++) { 2741 overlap[k] |= geom_uses_dimTag(geometries[k], dim, tag); 2742 } 2743 } 2744 } 2745 if(ov) { 2746 size_t k, item_cpt = 0; 2747 res_T tmp_err; 2748 if(dump_overlapping_err) { 2749 ERR(scad_scene_mesh()); 2750 } 2751 for(k = 0; k < geometries_count; k++) { 2752 struct scad_geometry* g = geometries[k]; 2753 if(!overlap[k]) 2754 continue; 2755 tmp_err = RES_BAD_OP; 2756 if(dump_overlapping_err) { 2757 if(str_is_empty(&g->name)) { 2758 str_printf(&tmp, "unamed_partition_error_%lu_%lu", 2759 err_cpt, (long unsigned)item_cpt++); 2760 tmp_err = 2761 scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0); 2762 if(tmp_err == RES_OK) { 2763 log_error(get_device(), 2764 "Unnamed geometry '%p' overlapping (dumped in '%s).\n", 2765 (void*)g, str_cget(&tmp)); 2766 } else { 2767 log_error(get_device(), "Could not dump geometry.\n"); 2768 } 2769 } else { 2770 str_printf(&tmp, "%s_partition_error_%lu_%lu", 2771 str_cget(&g->name), err_cpt, (long unsigned)item_cpt++); 2772 tmp_err = 2773 scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0); 2774 if(tmp_err == RES_OK) { 2775 log_error(get_device(), 2776 "Geometry '%s' overlapping (dumped in '%s).\n", 2777 str_cget(&g->name), str_cget(&tmp)); 2778 } else { 2779 log_error(get_device(), "Could not dump geometry.\n"); 2780 } 2781 } 2782 } 2783 if(tmp_err != RES_OK) { /* not dumped */ 2784 if(str_is_empty(&g->name)) { 2785 log_error(get_device(), "Unnamed geometry '%p' overlapping.\n", 2786 (void*)g); 2787 } else { 2788 log_error(get_device(), "Geometry '%s' overlapping.\n", 2789 str_cget(&g->name)); 2790 } 2791 } 2792 } 2793 err_cpt++; 2794 } 2795 if(ov) { 2796 res = RES_BAD_ARG; 2797 goto error; 2798 } 2799 } 2800 } 2801 2802 /* Create htables (mappings are used to ease access) */ 2803 htable_mappings_init(allocator, &m2); 2804 htable_mappings_init(allocator, &m3); 2805 htable_tags_init(allocator, &t2); 2806 htable_tags_init(allocator, &t3); 2807 ht_initialized = 1; 2808 for(i = 0; i < sz; i += 2) { 2809 int dim = data[i]; 2810 int tag = data[i+1]; 2811 size_t mapping = i/2; 2812 struct htable_mappings* mn = (dim == 2) ? &m2 : &m3; 2813 ASSERT(dim == 2 || dim == 3); 2814 ERR(htable_mappings_set(mn, &tag, &mapping)); 2815 } 2816 2817 for(i = 0; i < geometries_count; i++) { 2818 struct scad_geometry* geom = geometries[i]; 2819 size_t c, n, j; 2820 int* dt = NULL; 2821 /* For each tag in geometries[i] out_geometries[i] includes the mapped tags. 2822 * Because of the overlapping case, the resulting tags need to be 2823 * deduplicated though. */ 2824 htable_tags_clear(&t2); 2825 htable_tags_clear(&t3); 2826 for(j = 0; j < geom->gmsh_dimTags_n; j += 2) { 2827 int dim = geom->gmsh_dimTags[j]; 2828 int tag = geom->gmsh_dimTags[j+1]; 2829 struct htable_mappings* mn = (dim == 2) ? &m2 : &m3; 2830 size_t k; 2831 size_t* mapping = htable_mappings_find(mn, &tag); 2832 ASSERT(dim == 2 || dim == 3); 2833 ASSERT(mapping && *mapping < mapnn); 2834 for(k = 0; k < mapn[*mapping]; k += 2) { 2835 char one = 1; 2836 int d = map[*mapping][k]; 2837 int t = map[*mapping][k+1]; 2838 struct htable_tags* tn = (d == 2) ? &t2 : &t3; 2839 ERR(htable_tags_set(tn, &t, &one)); 2840 } 2841 } 2842 /* Allocate result */ 2843 n = htable_tags_size_get(&t2) + htable_tags_size_get(&t3); 2844 dt = MEM_ALLOC(allocator, sizeof(*dt) * 2 * n); 2845 if(!dt) { 2846 res = RES_MEM_ERR; 2847 goto error; 2848 } 2849 /* Copy tags */ 2850 c = 0; 2851 htable_tags_begin(&t2, &it); 2852 htable_tags_end(&t2, &end); 2853 while(!htable_tags_iterator_eq(&it, &end)) { 2854 dt[c++] = 2; 2855 dt[c++] = *htable_tags_iterator_key_get(&it); 2856 htable_tags_iterator_next(&it); 2857 } 2858 htable_tags_begin(&t3, &it); 2859 htable_tags_end(&t3, &end); 2860 while(!htable_tags_iterator_eq(&it, &end)) { 2861 dt[c++] = 3; 2862 dt[c++] = *htable_tags_iterator_key_get(&it); 2863 htable_tags_iterator_next(&it); 2864 } 2865 ASSERT(c == 2*n); 2866 2867 /* Create geometry */ 2868 ERR(geometry_create(geoms+i)); 2869 geoms[i]->gmsh_dimTags_n = c; 2870 geoms[i]->gmsh_dimTags = dt; 2871 ERR(device_register_tags(geoms[i])); 2872 } 2873 memcpy(out_geometries, geoms, geometries_count * sizeof(*geoms)); 2874 2875 exit: 2876 gmshFree(mapn); 2877 free_gmsh_map(map, mapnn); 2878 if(ht_initialized) { 2879 htable_mappings_release(&m2); 2880 htable_mappings_release(&m3); 2881 htable_tags_release(&t2); 2882 htable_tags_release(&t3); 2883 } 2884 if(tmp_initialized) str_release(&tmp); 2885 if(allocator) { 2886 MEM_RM(allocator, data); 2887 MEM_RM(allocator, geoms); 2888 MEM_RM(allocator, overlap); 2889 } 2890 gmshFree(tagout); 2891 return res; 2892 error: 2893 if(geoms) { 2894 for(i = 0; i < geometries_count; i++) { 2895 if(geoms[i]) { 2896 SCAD(geometry_ref_put(geoms[i])); 2897 } 2898 } 2899 } 2900 if(tagout) { 2901 gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 2902 /* Do not check ierr, first because we are in error handler already, but 2903 * also because an error is expected if these OCC entities have allready 2904 * been freed through the call to ref_put above */ 2905 } 2906 goto exit; 2907 } 2908 2909 res_T 2910 scad_geometries_swap 2911 (struct scad_geometry** pool1, 2912 struct scad_geometry** pool2, 2913 const size_t count, 2914 const int flags) 2915 { 2916 res_T res = RES_OK; 2917 struct scad_device* dev = get_device(); 2918 size_t i; 2919 struct mem_allocator* allocator = NULL; 2920 struct str tmp, msg; 2921 2922 if(!pool1 || !pool2 || !flags || count == 0) { 2923 res = RES_BAD_ARG; 2924 goto error; 2925 } 2926 2927 ERR(check_device(FUNC_NAME)); 2928 allocator = dev->allocator; 2929 if(flags & SCAD_SWAP_NAME) str_init(allocator, &tmp); 2930 if(flags & SCAD_SWAP_GEOMETRY && dev->log) str_init(allocator, &msg); 2931 for(i = 0; i < count; i++) { 2932 struct scad_geometry *g1 = pool1[i], *g2 = pool2[i]; 2933 size_t c1 = g1->gmsh_dimTags_n, c2 = g2->gmsh_dimTags_n; 2934 int *dt1 = g1->gmsh_dimTags, *dt2 = g2->gmsh_dimTags; 2935 if(pool1[i] == pool2[i]) 2936 continue; 2937 /* Swap content according to flags. Don't swap refcount! */ 2938 if(flags & SCAD_SWAP_NAME) { 2939 if(dev->log) { 2940 if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) { 2941 /* Do nothing */ 2942 } 2943 else if(str_is_empty(&g1->name)) { 2944 logger_print(dev->logger, dev->log_type, 2945 "Swapping names for geometry %p and geometry '%s'.\n", 2946 (void*)g1, str_cget(&g2->name)); 2947 logger_print(dev->logger, dev->log_type, 2948 "Geometry '%s' is now unnamed geometry %p.\n", 2949 str_cget(&g2->name), (void*)g2); 2950 } 2951 else if(str_is_empty(&g2->name)) { 2952 logger_print(dev->logger, dev->log_type, 2953 "Swapping names for geometry %p and geometry '%s'.\n", 2954 (void*)g2, str_cget(&g1->name)); 2955 logger_print(dev->logger, dev->log_type, 2956 "Geometry '%s' is now unnamed geometry %p.\n", 2957 str_cget(&g1->name), (void*)g1); 2958 } else { /* Both named */ 2959 logger_print(dev->logger, dev->log_type, 2960 "Swapping names for geometries '%s' and '%s'.\n", 2961 str_cget(&g1->name), str_cget(&g1->name)); 2962 } 2963 } 2964 if(!str_is_empty(&g1->name)) { 2965 ERR(htable_names_set(&dev->geometry_names, &g1->name, &g2)); 2966 } 2967 if(!str_is_empty(&g2->name)) { 2968 ERR(htable_names_set(&dev->geometry_names, &g2->name, &g1)); 2969 } 2970 if(!str_is_empty(&g1->name) || !str_is_empty(&g2->name)) { 2971 ERR(str_copy(&tmp, &g1->name)); 2972 ERR(str_copy(&g1->name, &g2->name)); 2973 ERR(str_copy(&g2->name, &tmp)); 2974 } 2975 } 2976 if(flags & SCAD_SWAP_GEOMETRY) { 2977 /* Swap in tag2geom tables */ 2978 size_t n; 2979 if(dev->log) { 2980 if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) { 2981 logger_print(dev->logger, dev->log_type, 2982 "Swapping tags for unnamed geometries %p and %p.\n", 2983 (void*)g1, (void*)g2); 2984 } 2985 else if(str_is_empty(&g1->name)) { 2986 logger_print(dev->logger, dev->log_type, 2987 "Swapping tags for unnamed geometry %p and geometry '%s'.\n", 2988 (void*)g1, str_cget(&g2->name)); 2989 } 2990 else if(str_is_empty(&g2->name)) { 2991 logger_print(dev->logger, dev->log_type, 2992 "Swapping tags for unnamed geometry %p and geometry '%s'.\n", 2993 (void*)g2, str_cget(&g1->name)); 2994 } 2995 else { 2996 logger_print(dev->logger, dev->log_type, 2997 "Swapping tags for geometries '%s' and '%s'.\n", 2998 str_cget(&g1->name), str_cget(&g1->name)); 2999 } 3000 if(str_is_empty(&g1->name)) { 3001 ERR(str_printf(&msg, 3002 "Tags now registered against unnamed geometry '%p': ", 3003 (void*)g1)); 3004 } else { 3005 ERR(str_printf(&msg, "Tags now registered against geometry '%s': ", 3006 str_cget(&g1->name))); 3007 } 3008 for(n = 0; n < c2; n += 2) { 3009 int dim = dt2[n]; 3010 int tag = dt2[n+1]; 3011 ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag)); 3012 } 3013 logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg)); 3014 if(str_is_empty(&g2->name)) { 3015 ERR(str_printf(&msg, 3016 "Tags now registered against unnamed geometry '%p': ", 3017 (void*)g2)); 3018 } else { 3019 ERR(str_printf(&msg, "Tags now registered against geometry '%s': ", 3020 str_cget(&g2->name))); 3021 } 3022 for(n = 0; n < c1; n += 2) { 3023 int dim = dt1[n]; 3024 int tag = dt1[n+1]; 3025 ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag)); 3026 } 3027 logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg)); 3028 } 3029 /* Swap tags */ 3030 SWAP(int*, g1->gmsh_dimTags, g2->gmsh_dimTags); 3031 SWAP(size_t, g1->gmsh_dimTags_n, g2->gmsh_dimTags_n); 3032 } 3033 } 3034 3035 exit: 3036 if(flags & SCAD_SWAP_NAME) str_release(&tmp); 3037 if(flags & SCAD_SWAP_GEOMETRY && dev->log) str_release(&msg); 3038 return res; 3039 error: 3040 goto exit; 3041 } 3042 3043 res_T 3044 scad_geometries_boundaries 3045 (struct scad_geometry** geometries, 3046 const size_t geometries_count, 3047 struct scad_geometry*** out_boundaries, 3048 size_t *out_count) 3049 { 3050 res_T res = RES_OK; 3051 int* tagout = NULL; 3052 size_t tagoutn, sz; 3053 int* data = NULL; 3054 int ierr = 0; 3055 struct scad_geometry** out_geom = NULL; 3056 struct mem_allocator* allocator = NULL; 3057 struct scad_device* dev = get_device(); 3058 int log; 3059 enum log_type log_type; 3060 size_t i, c = 0, n; 3061 struct str msg; 3062 int msg_initialized = 0; 3063 3064 if(!geometries || geometries_count == 0 || !out_boundaries || !out_count) { 3065 res = RES_BAD_ARG; 3066 goto error; 3067 } 3068 3069 ERR(check_device(FUNC_NAME)); 3070 allocator = dev->allocator; 3071 3072 ERR(gather_tags(geometries, geometries_count, &data, &sz)); 3073 ERR(sync_device()); 3074 gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr); 3075 ERR(gmsh_err_to_res_T(ierr)); 3076 3077 ASSERT(tagoutn % 2 == 0); 3078 c = tagoutn / 2; 3079 out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom)); 3080 if(!out_geom) { 3081 res = RES_MEM_ERR; 3082 goto error; 3083 } 3084 3085 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 3086 log_type = dev->log_type; 3087 if(log) { 3088 str_init(allocator, &msg); 3089 msg_initialized = 1; 3090 logger_print(dev->logger, log_type, "Boundary specific tag management:\n"); 3091 ERR(str_printf(&msg, " tags [")); 3092 for(i = 0; i < tagoutn; i += 2) { 3093 const int dim = tagout[i]; 3094 const int tag = tagout[i+1]; 3095 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 3096 } 3097 ERR(str_append_printf(&msg, "] getting a ref to tags [")); 3098 for(i = 0; i < sz; i += 2) { 3099 const int dim = data[i]; 3100 const int tag = data[i+1]; 3101 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 3102 } 3103 logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); 3104 } 3105 3106 for(i = 0, n = 0; i < tagoutn; i += 2, n++) { 3107 int dim = tagout[i]; 3108 int tag = tagout[i+1]; 3109 struct tag_desc* desc = NULL; 3110 ASSERT(dim == 2); 3111 ERR(geometry_create(out_geom+n)); 3112 out_geom[n]->gmsh_dimTags_n = 2; 3113 out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*tagout)); 3114 if(!out_geom[n]->gmsh_dimTags) { 3115 res = RES_MEM_ERR; 3116 goto error; 3117 } 3118 out_geom[n]->gmsh_dimTags[0] = dim; 3119 out_geom[n]->gmsh_dimTags[1] = tag; 3120 3121 ERR(device_register_tags(out_geom[n])); 3122 3123 /* Need to protect out_geometry's tags by getting a ref on input tags or 3124 * deleting input geometry will possibly delete them */ 3125 ERR(device_register_ref_to_tags(dim, tag, data, sz)); 3126 /* As the 2D tags will be deleted when the 3D tag they are part of are 3127 * deleted, they shouldn't be deleted when the geometry they belongs to are 3128 * released. */ 3129 desc = device_get_description(dim, tag); 3130 ASSERT(desc); 3131 desc->delete_policy = Scad_do_not_delete; 3132 } 3133 3134 exit: 3135 if(msg_initialized) str_release(&msg); 3136 if(allocator) MEM_RM(allocator, data); 3137 if(out_boundaries) *out_boundaries = out_geom; 3138 if(out_count) *out_count = c; 3139 gmshFree(tagout); 3140 return res; 3141 error: 3142 if(out_geom) { 3143 for(i = 0; i < c; i++) { 3144 if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i])); 3145 } 3146 MEM_RM(allocator, out_geom); 3147 out_geom = NULL; 3148 c = 0; 3149 } 3150 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 3151 goto exit; 3152 } 3153 3154 3155 res_T 3156 scad_geometries_boundary 3157 (struct scad_geometry** geometries, 3158 const size_t geometries_count, 3159 struct scad_geometry** out_boundary) 3160 { 3161 res_T res = RES_OK; 3162 int* tagout = NULL; 3163 size_t tagoutn, sz; 3164 int* data = NULL; 3165 int ierr = 0; 3166 struct scad_geometry* out_geom = NULL; 3167 struct mem_allocator* allocator = NULL; 3168 struct scad_device* dev = get_device(); 3169 int log; 3170 enum log_type log_type; 3171 size_t i; 3172 struct str msg; 3173 int msg_initialized = 0; 3174 3175 if(!geometries || geometries_count == 0 || !out_boundary) { 3176 res = RES_BAD_ARG; 3177 goto error; 3178 } 3179 3180 ERR(check_device(FUNC_NAME)); 3181 allocator = dev->allocator; 3182 3183 ERR(gather_tags(geometries, geometries_count, &data, &sz)); 3184 ERR(sync_device()); 3185 gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr); 3186 ERR(gmsh_err_to_res_T(ierr)); 3187 ASSERT(tagoutn % 2 == 0); 3188 3189 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 3190 log_type = dev->log_type; 3191 if(log) { 3192 str_init(allocator, &msg); 3193 msg_initialized = 1; 3194 logger_print(dev->logger, log_type, "Boundary specific tag management:\n"); 3195 ERR(str_printf(&msg, " tags [")); 3196 for(i = 0; i < tagoutn; i += 2) { 3197 const int dim = tagout[i]; 3198 const int tag = tagout[i+1]; 3199 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 3200 } 3201 ERR(str_append_printf(&msg, "] getting a ref to tags [")); 3202 for(i = 0; i < sz; i += 2) { 3203 const int dim = data[i]; 3204 const int tag = data[i+1]; 3205 ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); 3206 } 3207 logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); 3208 } 3209 3210 ERR(geometry_create(&out_geom)); 3211 out_geom->gmsh_dimTags_n = tagoutn; 3212 out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); 3213 if(!out_geom->gmsh_dimTags) { 3214 res = RES_MEM_ERR; 3215 goto error; 3216 } 3217 memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); 3218 ERR(device_register_tags(out_geom)); 3219 for(i = 0; i < out_geom->gmsh_dimTags_n; i += 2) { 3220 const int dim = out_geom->gmsh_dimTags[i]; 3221 const int tag = out_geom->gmsh_dimTags[i+1]; 3222 struct tag_desc* desc = device_get_description(dim, tag); 3223 ASSERT(dim == 2); 3224 ASSERT(desc != NULL); 3225 /* Need to protect input geometry's tags by getting a ref on output tags or 3226 * deleting out_geometry will possibly delete them */ 3227 ERR(device_register_ref_to_tags(dim, tag, data, sz)); 3228 /* As the 2D tags will be deleted when the 3D tag they are part of are 3229 * deleted, they shouldn't be deleted when the geometry they belongs to is 3230 * released. */ 3231 desc->delete_policy = Scad_do_not_delete; 3232 } 3233 3234 exit: 3235 if(msg_initialized) str_release(&msg); 3236 if(allocator) MEM_RM(allocator, data); 3237 if(out_boundary) *out_boundary = out_geom; 3238 gmshFree(tagout); 3239 return res; 3240 error: 3241 if(out_geom) { 3242 SCAD(geometry_ref_put(out_geom)); 3243 out_geom = NULL; 3244 } 3245 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 3246 goto exit; 3247 } 3248 3249 res_T 3250 scad_step_import 3251 (const char* filename, 3252 struct scad_geometry*** out_geometry, 3253 size_t* out_n) 3254 { 3255 int ierr; 3256 int* tagout = NULL; 3257 size_t tagoutn, i, ga_sz; 3258 struct str strname; 3259 int name_initialized = 0; 3260 struct scad_geometry** geom_array = NULL; 3261 struct mem_allocator* allocator = NULL; 3262 struct scad_device* dev = get_device(); 3263 res_T res = RES_OK; 3264 3265 if(!filename || !out_geometry || !out_n) { 3266 res = RES_BAD_ARG; 3267 goto error; 3268 } 3269 3270 ERR(check_device(FUNC_NAME)); 3271 allocator = dev->allocator; 3272 3273 gmshModelOccImportShapes(filename, &tagout, &tagoutn, 1, "step", &ierr); 3274 ERR(gmsh_err_to_res_T(ierr)); 3275 3276 ASSERT(tagoutn % 2 == 0); 3277 ga_sz = tagoutn / 2; 3278 allocator = get_device()->allocator; 3279 geom_array = MEM_CALLOC(allocator, ga_sz, sizeof(*geom_array)); 3280 if(!geom_array) { 3281 res = RES_MEM_ERR; 3282 goto error; 3283 } 3284 3285 str_init(allocator, &strname); 3286 name_initialized = 1; 3287 for(i=0; i<ga_sz; ++i) { 3288 ERR(geometry_create(geom_array+i)); 3289 geom_array[i]->gmsh_dimTags_n = 2; 3290 geom_array[i]->gmsh_dimTags 3291 = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags)); 3292 if(!geom_array[i]->gmsh_dimTags) { 3293 res = RES_MEM_ERR; 3294 goto error; 3295 } 3296 geom_array[i]->gmsh_dimTags[0] = tagout[2*i]; 3297 geom_array[i]->gmsh_dimTags[1] = tagout[2*i+1]; 3298 3299 ERR(device_register_tags(geom_array[i])); 3300 } 3301 3302 exit: 3303 gmshFree(tagout); 3304 if(out_n) *out_n = ga_sz ; 3305 if(out_geometry) *out_geometry = geom_array; 3306 if(name_initialized) str_release(&strname); 3307 return res; 3308 error: 3309 if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); 3310 ga_sz = 0; 3311 if(geom_array) { 3312 for(i=0; i<ga_sz; ++i) { 3313 if(geom_array[i]) SCAD(geometry_ref_put(geom_array[i])); 3314 } 3315 MEM_RM(allocator, geom_array); 3316 geom_array = NULL; 3317 } 3318 goto exit; 3319 } 3320 3321 res_T 3322 scad_geometry_get_normal 3323 (struct scad_geometry* geom, 3324 const double p[3], 3325 double N[3], 3326 struct scad_geometry** out_geometry) 3327 { 3328 res_T res = RES_OK; 3329 int ierr = 0; 3330 size_t i; 3331 const int* data = NULL; 3332 size_t sz = 0; 3333 struct scad_geometry* out = NULL; 3334 struct scad_device* dev = get_device(); 3335 int log; 3336 enum log_type log_type; 3337 struct mem_allocator* allocator = NULL; 3338 double* coord = NULL; 3339 double* pcoord = NULL; 3340 double* normals = NULL; 3341 struct darray_int tags; 3342 int initialized = 0; 3343 double d, min_d = DBL_MAX, pcoord_min[2]; 3344 int min_tag = -1; 3345 size_t normals_n; 3346 3347 if(!geom || !p || !N) { 3348 res = RES_BAD_ARG; 3349 goto error; 3350 } 3351 3352 ERR(check_device(FUNC_NAME)); 3353 allocator = dev->allocator; 3354 3355 darray_int_init(dev->allocator, &tags); 3356 initialized = 1; 3357 3358 ERR(get_2d_tags(geom, &tags)); 3359 data = darray_int_cdata_get(&tags); 3360 sz = darray_int_size_get(&tags); 3361 3362 log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); 3363 log_type = dev->log_type; 3364 3365 /* Find the closest point on tags of geom */ 3366 for(i = 0; i < sz; ++i) { 3367 size_t pcoord_n; 3368 size_t coord_n; 3369 const int dim = 2; 3370 int tag = data[i]; 3371 double tmp[3]; 3372 3373 gmshModelGetClosestPoint(dim, tag, p, 3, &coord, &coord_n, &pcoord, 3374 &pcoord_n, &ierr); 3375 ERR(gmsh_err_to_res_T(ierr)); 3376 ASSERT(pcoord_n == (size_t)dim); 3377 ASSERT(coord_n == 3); 3378 d = d3_len(d3_sub(tmp, p, coord)); 3379 if(d < min_d) { 3380 min_d = d; 3381 min_tag = tag; 3382 d2_set(pcoord_min, pcoord); 3383 } 3384 gmshFree(coord); 3385 gmshFree(pcoord); 3386 coord = pcoord = NULL; 3387 } 3388 if(min_d == DBL_MAX) { /* At least if sz==0 */ 3389 goto exit; 3390 } 3391 3392 /* Get the normal at the selected point */ 3393 gmshModelGetNormal(min_tag, pcoord_min, 2, &normals, &normals_n, &ierr); 3394 ERR(gmsh_err_to_res_T(ierr)); 3395 ASSERT(normals_n == 3); 3396 d3_set(N, normals); 3397 3398 /* Create a geometry if required */ 3399 if(out_geometry) { 3400 ERR(geometry_create(&out)); 3401 out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags)); 3402 if(!out->gmsh_dimTags) { 3403 res = RES_MEM_ERR; 3404 goto error; 3405 } 3406 out->gmsh_dimTags_n = 2; 3407 out->gmsh_dimTags[0] = 2; 3408 out->gmsh_dimTags[1] = min_tag; 3409 ERR(device_register_tags(out)); 3410 3411 /* Need to protect geometries' tags or deleting out geometry will possibly 3412 * delete them */ 3413 if(log) { 3414 logger_print(dev->logger, log_type, 3415 "Tag %d.%d getting a reference to other tags.\n", 2, min_tag); 3416 } 3417 ERR(device_register_ref_to_tags(2, min_tag, geom->gmsh_dimTags, 3418 geom->gmsh_dimTags_n)); 3419 if(log) { 3420 logger_print(dev->logger, log_type, 3421 "Tag %d.%d getting a reference to other tags done.\n", 2, min_tag); 3422 } 3423 } 3424 3425 exit: 3426 gmshFree(coord); 3427 gmshFree(pcoord); 3428 gmshFree(normals); 3429 if(initialized) darray_int_release(&tags); 3430 if(out_geometry) *out_geometry = out; 3431 return res; 3432 error: 3433 if(out) { 3434 SCAD(geometry_ref_put(out)); 3435 out = NULL; 3436 } 3437 goto exit; 3438 } 3439 3440 res_T 3441 scad_geometry_dilate 3442 (const struct scad_geometry* geom, 3443 const double center[3], 3444 const double scale[3], 3445 struct scad_geometry** out_geometry) 3446 { 3447 res_T res = RES_OK; 3448 int ierr = 0; 3449 struct scad_geometry* out = NULL; 3450 3451 if(!geom || !scale|| !center || ! out_geometry) { 3452 res = RES_BAD_ARG; 3453 goto error; 3454 } 3455 3456 ERR(check_device(FUNC_NAME)); 3457 3458 ERR(scad_geometry_copy(geom, &out)); 3459 3460 gmshModelOccDilate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(center), 3461 SPLIT3(scale), &ierr); 3462 ERR(gmsh_err_to_res_T(ierr)); 3463 3464 exit: 3465 if(out_geometry) *out_geometry = out; 3466 return res; 3467 error: 3468 if(out) SCAD(geometry_ref_put(out)); 3469 out = NULL; 3470 goto exit; 3471 } 3472 3473 res_T 3474 scad_geometries_set_mesh_size_modifier 3475 (struct scad_geometry** geometries, 3476 const size_t geometries_count, 3477 enum scad_size_modifier_type type, 3478 double modifier) 3479 { 3480 res_T res = RES_OK; 3481 struct scad_device* dev = get_device(); 3482 int ierr, dim, some = 0; 3483 int* tagout[4] = { NULL, NULL, NULL, NULL }; 3484 size_t tagoutn[4] = { 0, 0, 0, 0}, i; 3485 3486 if(!geometries || geometries_count == 0 || modifier <= 0 3487 || (type != SCAD_ABSOLUTE_SIZE && type != SCAD_SIZE_FACTOR)) 3488 { 3489 res = RES_BAD_ARG; 3490 goto error; 3491 } 3492 3493 ERR(check_device(FUNC_NAME)); 3494 3495 if(type == SCAD_ABSOLUTE_SIZE) modifier = -modifier; 3496 ERR(gather_tags_recursive(geometries, geometries_count, 0, tagout, tagoutn)); 3497 for(dim = 0; dim < 4; dim++) { 3498 for(i = 0; i < tagoutn[dim]; i += 2) { 3499 int d = tagout[dim][i]; 3500 int tag = tagout[dim][i+1]; 3501 struct htable_size_modifiers* modifiers = dev->size_modifiers_by_dim + dim; 3502 double* f_ptr = htable_size_modifiers_find(modifiers, &tag); 3503 ASSERT(d == dim);(void)d; 3504 if(f_ptr && modifier == 1) { /* Size factor of 1 => no modifier */ 3505 size_t c = htable_size_modifiers_erase(modifiers, &tag); 3506 ASSERT(c == 1);(void)c; 3507 } else if(f_ptr) { 3508 *f_ptr = modifier; 3509 } else { 3510 ERR(htable_size_modifiers_set(modifiers, &tag, &modifier)); 3511 } 3512 } 3513 } 3514 for(dim = 0; dim < 4; dim++) { 3515 if(htable_size_modifiers_size_get(dev->size_modifiers_by_dim + dim) > 0) { 3516 some = 1; 3517 break; 3518 } 3519 } 3520 3521 gmshModelMeshSetSizeCallback((some ? size_callback : NULL), NULL, &ierr); 3522 ERR(gmsh_err_to_res_T(ierr)); 3523 3524 exit: 3525 if(dev) { 3526 for(dim = 0; dim < 4; dim++) { 3527 MEM_RM(dev->allocator, tagout[dim]); 3528 } 3529 } 3530 return res; 3531 error: 3532 goto exit; 3533 } 3534 3535 res_T 3536 scad_geometries_set_mesh_algorithm 3537 (struct scad_geometry** geometries, 3538 const size_t geometries_count, 3539 enum scad_mesh_algorithm algorithm) 3540 { 3541 res_T res = RES_OK; 3542 struct scad_device* dev = get_device(); 3543 int ierr; 3544 int* tagout[4] = { NULL, NULL, NULL, NULL }; 3545 size_t tagoutn[4], i; 3546 3547 if(!geometries || geometries_count == 0 || 3548 (algorithm != SCAD_MESHADAPT && 3549 algorithm != SCAD_AUTOMATIC && 3550 algorithm != SCAD_INITIAL_MESH_ONLY && 3551 algorithm != SCAD_DELAUNAY && 3552 algorithm != SCAD_FRONTAL_DELAUNAY && 3553 algorithm != SCAD_QUASI_STRUCTURED)) 3554 { 3555 res = RES_BAD_ARG; 3556 goto error; 3557 } 3558 3559 ERR(check_device(FUNC_NAME)); 3560 3561 ERR(gather_tags_recursive(geometries, geometries_count, 2, tagout, tagoutn)); 3562 for(i = 0; i < tagoutn[2]; i += 2) { 3563 int dim = tagout[2][i]; 3564 int tag = tagout[2][i+1]; 3565 gmshModelMeshSetAlgorithm(dim, tag, (int)algorithm, &ierr); 3566 ERR(gmsh_err_to_res_T(ierr)); 3567 } 3568 3569 exit: 3570 if(dev) { 3571 for(i = 2; i < 4; i++) { 3572 MEM_RM(dev->allocator, tagout[i]); 3573 } 3574 } 3575 return res; 3576 error: 3577 goto exit; 3578 } 3579 3580 res_T 3581 scad_geometries_set_periodic 3582 (struct scad_geometry** source, 3583 const size_t source_count, 3584 struct scad_geometry** target, 3585 const size_t target_count, 3586 double affine[16]) 3587 { 3588 res_T res = RES_OK; 3589 struct scad_device* dev = get_device(); 3590 struct mem_allocator* allocator = NULL; 3591 int ierr; 3592 int* src_dimTagout[4] = { NULL, NULL, NULL, NULL }; 3593 int* tgt_dimTagout[4] = { NULL, NULL, NULL, NULL }; 3594 int* src_tags = NULL, *tgt_tags = NULL; 3595 size_t src_dimTagoutn[4] = { 0,0,0,0 }, tgt_dimTagoutn[4] = { 0,0,0,0 }; 3596 size_t src_tagsn = 0, tgt_tagsn = 0, i; 3597 3598 if(!source || source_count == 0 || !target || target_count == 0 || !affine) { 3599 res = RES_BAD_ARG; 3600 goto error; 3601 } 3602 3603 ERR(check_device(FUNC_NAME)); 3604 3605 ERR(gather_tags_recursive(source, source_count, 2, src_dimTagout, src_dimTagoutn)); 3606 ERR(gather_tags_recursive(target, target_count, 2, tgt_dimTagout, tgt_dimTagoutn)); 3607 ASSERT(src_dimTagoutn[2] % 2 == 0 && tgt_dimTagoutn[2] % 2 == 0); 3608 src_tagsn = src_dimTagoutn[2] / 2; 3609 tgt_tagsn = tgt_dimTagoutn[2] / 2; 3610 if(src_tagsn == 0 || tgt_tagsn == 0) { 3611 res = RES_BAD_ARG; 3612 goto error; 3613 } 3614 3615 allocator = dev->allocator; 3616 src_tags = MEM_ALLOC(allocator, src_tagsn * sizeof(*src_tags)); 3617 tgt_tags = MEM_ALLOC(allocator, tgt_tagsn * sizeof(*tgt_tags)); 3618 if(!src_tags || !tgt_tags) { 3619 res = RES_MEM_ERR; 3620 goto error; 3621 } 3622 for(i = 0; i < src_tagsn; i += 2) { 3623 src_tags[i] = src_dimTagout[2][i+1]; 3624 } 3625 for(i = 0; i < tgt_tagsn; i += 2) { 3626 tgt_tags[i] = tgt_dimTagout[2][i+1]; 3627 } 3628 gmshModelMeshSetPeriodic(2, tgt_tags, tgt_tagsn, src_tags, src_tagsn, 3629 affine, 16, &ierr); 3630 ERR(gmsh_err_to_res_T(ierr)); 3631 3632 exit: 3633 if(dev) { 3634 for(i = 2; i < 4; i++) { 3635 MEM_RM(dev->allocator, src_dimTagout[i]); 3636 MEM_RM(dev->allocator, tgt_dimTagout[i]); 3637 } 3638 MEM_RM(dev->allocator, src_tags); 3639 MEM_RM(dev->allocator, tgt_tags); 3640 } 3641 return res; 3642 error: 3643 goto exit; 3644 }