Downsample the surface if isomorphic to a subdivided icosahedron.
(surf, dest_grade)
| 454 | |
| 455 | |
| 456 | def _ico_downsample(surf, dest_grade): |
| 457 | """Downsample the surface if isomorphic to a subdivided icosahedron.""" |
| 458 | n_tri = len(surf["tris"]) |
| 459 | bad_msg = ( |
| 460 | f"Cannot decimate to requested ico grade {dest_grade}. The provided " |
| 461 | f"BEM surface has {n_tri} triangles, which cannot be isomorphic with " |
| 462 | "a subdivided icosahedron. Consider manually decimating the surface to " |
| 463 | "a suitable density and then use ico=None in make_bem_model." |
| 464 | ) |
| 465 | if n_tri % 20 != 0: |
| 466 | raise RuntimeError(bad_msg) |
| 467 | n_tri = n_tri // 20 |
| 468 | found = int(round(np.log(n_tri) / np.log(4))) |
| 469 | if n_tri != 4**found: |
| 470 | raise RuntimeError(bad_msg) |
| 471 | del n_tri |
| 472 | |
| 473 | if dest_grade > found: |
| 474 | raise RuntimeError( |
| 475 | f"For this surface, decimation grade should be {found} or less, " |
| 476 | f"not {dest_grade}." |
| 477 | ) |
| 478 | |
| 479 | source = _get_ico_surface(found) |
| 480 | dest = _get_ico_surface(dest_grade, patch_stats=True) |
| 481 | del dest["tri_cent"] |
| 482 | del dest["tri_nn"] |
| 483 | del dest["neighbor_tri"] |
| 484 | del dest["tri_area"] |
| 485 | if not np.array_equal(source["tris"], surf["tris"]): |
| 486 | raise RuntimeError( |
| 487 | "The source surface has a matching number of " |
| 488 | "triangles but ordering is wrong" |
| 489 | ) |
| 490 | logger.info( |
| 491 | f"Going from {found}th to {dest_grade}th subdivision of an icosahedron " |
| 492 | f"(n_tri: {len(surf['tris'])} -> {len(dest['tris'])})" |
| 493 | ) |
| 494 | # Find the mapping |
| 495 | dest["rr"] = surf["rr"][_get_ico_map(source, dest)] |
| 496 | return dest |
| 497 | |
| 498 | |
| 499 | def _get_ico_map(fro, to): |