test_surface.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. import copy
  2. import datetime
  3. import unittest.mock
  4. import numpy
  5. import pytest
  6. from conftest import ANNOTATION_FILE_PATH, SURFACE_FILE_PATH
  7. from freesurfer_surface import (
  8. Annotation,
  9. LineSegment,
  10. PolygonalCircuit,
  11. Surface,
  12. Triangle,
  13. Vertex,
  14. setlocale,
  15. )
  16. # pylint: disable=protected-access
  17. def test_read_triangular():
  18. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  19. assert surface.creator == b"fabianpeter"
  20. assert surface.creation_datetime == datetime.datetime(2019, 5, 9, 22, 37, 41)
  21. assert len(surface.vertices) == 155622
  22. assert len(surface.triangles) == 311240
  23. assert not surface.using_old_real_ras
  24. assert surface.volume_geometry_info == (
  25. b"valid = 1 # volume info valid\n",
  26. b"filename = ../mri/filled-pretess255.mgz\n",
  27. b"volume = 256 256 256\n",
  28. b"voxelsize = 1.000000000000000e+00 1.000000000000000e+00 1.000000000000000e+00\n",
  29. b"xras = -1.000000000000000e+00 0.000000000000000e+00 1.862645149230957e-09\n",
  30. b"yras = 0.000000000000000e+00 -6.655682227574289e-09 -1.000000000000000e+00\n",
  31. b"zras = 0.000000000000000e+00 1.000000000000000e+00 -8.300048648379743e-09\n",
  32. b"cras = -2.773597717285156e+00 1.566547393798828e+01 -7.504364013671875e+00\n",
  33. )
  34. assert surface.command_lines == [
  35. b"mris_remove_intersection ../surf/lh.orig ../surf/lh.orig"
  36. b" ProgramVersion: $Name: stable6 $"
  37. b" TimeStamp: 2019/05/09-17:42:36-GMT"
  38. b" BuildTimeStamp: Jan 18 2017 16:38:58"
  39. b" CVS: $Id: mris_remove_intersection.c,v 1.6 2011/03/02 00:04:32 nicks Exp $"
  40. b" User: fabianpeter"
  41. b" Machine: host12345"
  42. b" Platform: Linux"
  43. b" PlatformVersion: 4.15.0-46-generic"
  44. b" CompilerName: GCC"
  45. b" CompilerVersion: 40400"
  46. b" ",
  47. b"mris_make_surfaces -orig_white white.preaparc -orig_pial white.preaparc"
  48. b" -aseg ../mri/aseg.presurf -mgz -T1 brain.finalsurfs"
  49. b" fabian20190509 lh ProgramVersion: $Name: $"
  50. b" TimeStamp: 2019/05/09-20:27:28-GMT"
  51. b" BuildTimeStamp: Jan 18 2017 16:38:58"
  52. b" CVS: $Id: mris_make_surfaces.c,v 1.164.2.4 2016/12/13 22:26:32 zkaufman Exp $"
  53. b" User: fabianpeter"
  54. b" Machine: host12345"
  55. b" Platform: Linux"
  56. b" PlatformVersion: 4.15.0-46-generic"
  57. b" CompilerName: GCC"
  58. b" CompilerVersion: 40400"
  59. b" ",
  60. ]
  61. def test_read_triangular_locale():
  62. with setlocale("de_AT.utf8"):
  63. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  64. assert surface.creation_datetime == datetime.datetime(2019, 5, 9, 22, 37, 41)
  65. @pytest.mark.parametrize(
  66. ("creation_datetime", "expected_str"),
  67. [
  68. (datetime.datetime(2019, 5, 9, 22, 37, 41), b"Thu May 9 22:37:41 2019"),
  69. (datetime.datetime(2019, 4, 24, 23, 29, 22), b"Wed Apr 24 23:29:22 2019"),
  70. ],
  71. )
  72. def test_triangular_strftime(creation_datetime, expected_str):
  73. # pylint: disable=protected-access
  74. assert expected_str == Surface._triangular_strftime(creation_datetime)
  75. def test_read_write_triangular_same(tmpdir):
  76. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  77. output_path = tmpdir.join("surface").strpath
  78. surface.write_triangular(output_path, creation_datetime=surface.creation_datetime)
  79. with open(output_path, "rb") as output_file:
  80. with open(SURFACE_FILE_PATH, "rb") as expected_file:
  81. assert expected_file.read() == output_file.read()
  82. def test_read_write_datetime(tmpdir):
  83. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  84. original_creation_datetime = surface.creation_datetime
  85. output_path = tmpdir.join("surface").strpath
  86. surface.write_triangular(output_path)
  87. assert original_creation_datetime == surface.creation_datetime
  88. new_surface = Surface.read_triangular(output_path)
  89. assert new_surface.creation_datetime > original_creation_datetime
  90. assert datetime.datetime.now() > new_surface.creation_datetime
  91. assert (
  92. datetime.datetime.now() - new_surface.creation_datetime
  93. ) < datetime.timedelta(seconds=20)
  94. def test_write_read_triangular_same(tmpdir):
  95. expected_surface = Surface()
  96. expected_surface.creator = b"pytest"
  97. expected_surface.creation_datetime = datetime.datetime.now().replace(microsecond=0)
  98. expected_surface.vertices = [
  99. Vertex(0.0, 0.0, 0.0),
  100. Vertex(1.0, 2.0, 3.0),
  101. Vertex(2.0, 4.0, 6.0),
  102. Vertex(3.0, 5.0, 7.0),
  103. ]
  104. expected_surface.triangles = [
  105. Triangle((0, 1, 2)),
  106. Triangle((0, 1, 3)),
  107. Triangle((3, 2, 1)),
  108. ]
  109. expected_surface.using_old_real_ras = False
  110. expected_surface.volume_geometry_info = tuple(b"?\n" for _ in range(8))
  111. expected_surface.command_lines = [b"?", b"!"]
  112. output_path = tmpdir.join("surface").strpath
  113. expected_surface.write_triangular(
  114. output_path, creation_datetime=expected_surface.creation_datetime
  115. )
  116. resulted_surface = Surface.read_triangular(output_path)
  117. assert numpy.array_equal(expected_surface.vertices, resulted_surface.vertices)
  118. expected_surface.vertices = resulted_surface.vertices = []
  119. assert vars(expected_surface) == vars(resulted_surface)
  120. def test_write_triangular_same_locale(tmpdir):
  121. surface = Surface()
  122. surface.creator = b"pytest"
  123. surface.volume_geometry_info = tuple(b"?" for _ in range(8))
  124. creation_datetime = datetime.datetime(2018, 12, 31, 21, 42)
  125. output_path = tmpdir.join("surface").strpath
  126. with setlocale("de_AT.utf8"):
  127. surface.write_triangular(output_path, creation_datetime=creation_datetime)
  128. resulted_surface = Surface.read_triangular(output_path)
  129. assert resulted_surface.creation_datetime == creation_datetime
  130. with open(output_path, "rb") as output_file:
  131. assert (
  132. output_file.read()
  133. .split(b" on ")[1]
  134. .startswith(b"Mon Dec 31 21:42:00 2018\n")
  135. )
  136. def test_load_annotation():
  137. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  138. assert not surface.annotation
  139. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  140. assert isinstance(surface.annotation, Annotation)
  141. assert len(surface.annotation.vertex_label_index) == 155622
  142. assert surface.annotation.vertex_label_index[0] == 5
  143. def test_add_vertex():
  144. surface = Surface()
  145. assert not surface.vertices
  146. assert surface.add_vertex(Vertex(1.0, 1.5, 2.0)) == 0
  147. assert len(surface.vertices) == 1
  148. assert surface.vertices[0].anterior == pytest.approx(1.5)
  149. assert surface.add_vertex(Vertex(-3.0, 0.0, 4.0)) == 1
  150. assert len(surface.vertices) == 2
  151. assert surface.vertices[1].right == pytest.approx(-3.0)
  152. @pytest.mark.parametrize(
  153. "vertices_coords",
  154. [
  155. ((0, 0, 0), (2, 4, 0), (2, 4, 3)),
  156. ((0, 0, 0), (2, 4, 0), (2, 4, 3), (0, 0, 3)),
  157. ((1, 1, 0), (3, 5, 0), (3, 5, 3), (1, 1, 3)),
  158. ((1, 1, 7), (3, 5, 7), (3, 5, 3), (1, 1, 3)),
  159. ((1, 1, 1), (3, 5, 7), (3, 5, 9), (1, 1, 3)),
  160. ((3, 5, 7), (1, 1, 1), (1, 1, 3)),
  161. ((3, 5, 7), (1, 1, 1), (1, 1, 3), (3, 5, 9)),
  162. ],
  163. )
  164. def test_add_rectangle(vertices_coords):
  165. surface = Surface()
  166. for vertex_coords in vertices_coords:
  167. surface.add_vertex(Vertex(*(float(c) for c in vertex_coords)))
  168. surface.add_rectangle(range(len(vertices_coords)))
  169. assert len(surface.vertices) == 4
  170. assert len(surface.triangles) == 2
  171. assert surface.triangles[0].vertex_indices == (0, 1, 2)
  172. assert surface.triangles[1].vertex_indices == (2, 3, 0)
  173. @pytest.mark.parametrize(
  174. ("vertices_coords", "expected_extra_vertex_coords"),
  175. [
  176. (((0, 0, 0), (2, 4, 0), (2, 4, 3)), (0, 0, 3)),
  177. (((1, 1, 0), (3, 5, 0), (3, 5, 3)), (1, 1, 3)),
  178. (((1, 1, 7), (3, 5, 7), (3, 5, 3)), (1, 1, 3)),
  179. (((1, 1, 1), (3, 5, 7), (3, 5, 9)), (1, 1, 3)),
  180. (((3, 5, 7), (1, 1, 1), (1, 1, 3)), (3, 5, 9)),
  181. ],
  182. )
  183. def test_add_rectangle_3(vertices_coords, expected_extra_vertex_coords):
  184. surface = Surface()
  185. for vertex_coords in vertices_coords:
  186. surface.add_vertex(Vertex(*(float(c) for c in vertex_coords)))
  187. surface.add_rectangle(range(3))
  188. assert tuple(surface.vertices[3]) == pytest.approx(expected_extra_vertex_coords)
  189. def test__triangle_count_by_adjacent_vertex_indices_empty():
  190. surface = Surface()
  191. assert surface._triangle_count_by_adjacent_vertex_indices() == {}
  192. def test__triangle_count_by_adjacent_vertex_indices_none():
  193. surface = Surface()
  194. surface.vertices.append(Vertex(1, 0, 0))
  195. surface.vertices.append(Vertex(2, 0, 0))
  196. surface.vertices.append(Vertex(3, 0, 0))
  197. assert surface._triangle_count_by_adjacent_vertex_indices() == {0: {}, 1: {}, 2: {}}
  198. def test__triangle_count_by_adjacent_vertex_indices_single():
  199. surface = Surface()
  200. surface.triangles.append(
  201. Triangle([surface.add_vertex(Vertex(i, 0, 0)) for i in range(3)])
  202. )
  203. assert surface._triangle_count_by_adjacent_vertex_indices() == {
  204. 0: {1: 1, 2: 1},
  205. 1: {0: 1, 2: 1},
  206. 2: {0: 1, 1: 1},
  207. }
  208. def test__triangle_count_by_adjacent_vertex_indices_multiple():
  209. surface = Surface()
  210. for i in range(5):
  211. surface.add_vertex(Vertex(i, 0, 0))
  212. surface.triangles.append(Triangle((0, 1, 2)))
  213. surface.triangles.append(Triangle((3, 1, 2)))
  214. assert surface._triangle_count_by_adjacent_vertex_indices() == {
  215. 0: {1: 1, 2: 1},
  216. 1: {0: 1, 2: 2, 3: 1},
  217. 2: {0: 1, 1: 2, 3: 1},
  218. 3: {1: 1, 2: 1},
  219. 4: {},
  220. }
  221. surface.triangles.append(Triangle((3, 4, 2)))
  222. assert surface._triangle_count_by_adjacent_vertex_indices() == {
  223. 0: {1: 1, 2: 1},
  224. 1: {0: 1, 2: 2, 3: 1},
  225. 2: {0: 1, 1: 2, 3: 2, 4: 1},
  226. 3: {1: 1, 2: 2, 4: 1},
  227. 4: {2: 1, 3: 1},
  228. }
  229. surface.triangles.append(Triangle((3, 0, 2)))
  230. assert surface._triangle_count_by_adjacent_vertex_indices() == {
  231. 0: {1: 1, 2: 2, 3: 1},
  232. 1: {0: 1, 2: 2, 3: 1},
  233. 2: {0: 2, 1: 2, 3: 3, 4: 1},
  234. 3: {0: 1, 1: 1, 2: 3, 4: 1},
  235. 4: {2: 1, 3: 1},
  236. }
  237. def test__triangle_count_by_adjacent_vertex_indices_real():
  238. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  239. counts = surface._triangle_count_by_adjacent_vertex_indices()
  240. assert len(counts) == len(surface.vertices)
  241. assert all(counts.values())
  242. assert all(
  243. count == 2
  244. for vertex_counts in counts.values()
  245. for count in vertex_counts.values()
  246. )
  247. assert (
  248. sum(
  249. count
  250. for vertex_counts in counts.values()
  251. for count in vertex_counts.values()
  252. )
  253. == len(surface.triangles) * 6
  254. )
  255. def test_find_borders_none():
  256. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  257. assert not list(surface.find_borders())
  258. def test_find_borders_single():
  259. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  260. single_index = surface.add_vertex(Vertex(0, 21, 42))
  261. borders = list(surface.find_borders())
  262. assert len(borders) == 1
  263. assert borders[0] == PolygonalCircuit((single_index,))
  264. def test_find_borders_singles():
  265. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  266. single_indices = [surface.add_vertex(Vertex(i, 21, 42)) for i in range(3)]
  267. borders = set(surface.find_borders())
  268. assert len(borders) == 3
  269. assert PolygonalCircuit((single_indices[0],)) in borders
  270. assert PolygonalCircuit((single_indices[1],)) in borders
  271. assert PolygonalCircuit((single_indices[2],)) in borders
  272. def test_find_borders_single_triangle_simple():
  273. surface = Surface()
  274. vertex_indices = [surface.add_vertex(Vertex(i, 21, 42)) for i in range(3)]
  275. surface.triangles.append(Triangle(vertex_indices))
  276. borders = set(surface.find_borders())
  277. assert len(borders) == 1
  278. assert PolygonalCircuit(vertex_indices) in borders
  279. def test_find_borders_single_triangle_real():
  280. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  281. vertex_indices = [surface.add_vertex(Vertex(i, 21, 42)) for i in range(3)]
  282. surface.triangles.append(Triangle(vertex_indices))
  283. borders = set(surface.find_borders())
  284. assert len(borders) == 1
  285. assert PolygonalCircuit(vertex_indices) in borders
  286. def test_find_borders_remove_triangle():
  287. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  288. triangle = surface.triangles.pop()
  289. borders = set(surface.find_borders())
  290. assert len(borders) == 1
  291. assert triangle in borders
  292. def test_find_borders_remove_non_adjacent_triangles():
  293. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  294. triangles = [surface.triangles.pop(), surface.triangles.pop()]
  295. borders = set(surface.find_borders())
  296. assert len(borders) == 2
  297. assert triangles[0] in borders
  298. assert triangles[1] in borders
  299. def test_find_borders_remove_adjacent_triangles():
  300. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  301. triangles = [surface.triangles.pop(), surface.triangles.pop()]
  302. triangles.append(surface.triangles.pop(270682))
  303. assert triangles[1] == Triangle((136141, 136142, 137076))
  304. assert triangles[2] == Triangle((136141, 136142, 135263))
  305. borders = set(surface.find_borders())
  306. assert len(borders) == 2
  307. assert triangles[0] in borders
  308. assert PolygonalCircuit((137076, 136141, 135263, 136142)) in borders
  309. surface.triangles.pop(270682)
  310. borders = set(surface.find_borders())
  311. assert len(borders) == 2
  312. assert triangles[0] in borders
  313. assert PolygonalCircuit((137076, 136141, 135263, 135264, 136142)) in borders
  314. surface.triangles.pop(274320)
  315. borders = set(surface.find_borders())
  316. assert len(borders) == 2
  317. assert PolygonalCircuit((136143, 138007, 138008, 137078)) in borders
  318. assert PolygonalCircuit((137076, 136141, 135263, 135264, 136142)) in borders
  319. @pytest.mark.parametrize(
  320. ("label_name", "expected_border_lens"),
  321. [
  322. ("precentral", [416]),
  323. ("postcentral", [395]),
  324. ("medialorbitofrontal", [6, 246]),
  325. # ...--2343 2347
  326. # \ / \
  327. # 2345 2348
  328. # / \ /
  329. # ...--2344 2346
  330. ("posteriorcingulate", [4, 190]),
  331. ("unknown", [3, 390]),
  332. ],
  333. )
  334. def test_find_borders_real(label_name, expected_border_lens):
  335. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  336. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  337. (label,) = filter(
  338. lambda l: l.name == label_name, surface.annotation.labels.values()
  339. )
  340. surface.triangles = list(
  341. filter(
  342. lambda t: all(
  343. surface.annotation.vertex_label_index[vertex_idx] == label.index
  344. for vertex_idx in t.vertex_indices
  345. ),
  346. surface.triangles,
  347. )
  348. )
  349. surface.remove_unused_vertices()
  350. borders = list(surface.find_borders())
  351. border_lens = [len(b.vertex_indices) for b in borders]
  352. # self-crossing borders may or may not be split into
  353. # separate polygonal circuits
  354. assert sorted(border_lens) == expected_border_lens or sum(border_lens) == sum(
  355. expected_border_lens
  356. )
  357. def test__get_vertex_label_index():
  358. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  359. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  360. # pylint: disable=protected-access
  361. assert surface._get_vertex_label_index(64290) == 22
  362. assert surface._get_vertex_label_index(72160) == 22
  363. assert surface._get_vertex_label_index(84028) == 24
  364. assert surface._get_vertex_label_index(97356) == 24
  365. assert surface._get_vertex_label_index(123173) == 27
  366. assert surface._get_vertex_label_index(140727) == 27
  367. assert surface._get_vertex_label_index(93859) == 28
  368. assert surface._get_vertex_label_index(78572) == 0
  369. assert surface._get_vertex_label_index(120377) == 0
  370. vertex_index = surface.add_vertex(Vertex(0.0, 21.0, 42.0))
  371. assert surface._get_vertex_label_index(vertex_index) is None
  372. del surface.annotation.vertex_label_index[140727]
  373. assert surface._get_vertex_label_index(140727) is None
  374. def test__find_label_border_segments():
  375. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  376. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  377. (precentral_label,) = filter(
  378. lambda l: l.name == "precentral", surface.annotation.labels.values()
  379. )
  380. # pylint: disable=protected-access
  381. border_segments = set(surface._find_label_border_segments(precentral_label))
  382. assert len(border_segments) == 417
  383. assert LineSegment((33450, 32065)) in border_segments
  384. assert LineSegment((33454, 33450)) in border_segments
  385. for border_vertex_index in [33450, 33454, 32065]:
  386. assert (
  387. surface.annotation.vertex_label_index[border_vertex_index]
  388. == precentral_label.index
  389. )
  390. for other_vertex_index in [32064, 33449, 33455, 33449, 33455]:
  391. assert (
  392. LineSegment((other_vertex_index, border_vertex_index))
  393. not in border_segments
  394. )
  395. assert (
  396. LineSegment((border_vertex_index, other_vertex_index))
  397. not in border_segments
  398. )
  399. def test__find_label_border_segments_incomplete_annotation():
  400. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  401. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  402. (precentral_label,) = filter(
  403. lambda l: l.name == "precentral", surface.annotation.labels.values()
  404. )
  405. # pylint: disable=protected-access
  406. assert surface._find_label_border_segments(precentral_label)
  407. surface.triangles.append(
  408. Triangle(
  409. [
  410. surface.add_vertex(Vertex(0.0, 21.0 * factor, 42.0 * factor))
  411. for factor in range(3)
  412. ]
  413. )
  414. )
  415. border_segments = set(surface._find_label_border_segments(precentral_label))
  416. assert len(border_segments) == 417
  417. def test_find_label_border_polygonal_chains():
  418. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  419. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  420. (precentral_label,) = filter(
  421. lambda l: l.name == "precentral", surface.annotation.labels.values()
  422. )
  423. (border_chain,) = surface.find_label_border_polygonal_chains(precentral_label)
  424. vertex_indices_normalized = list(border_chain.normalized().vertex_indices)
  425. assert len(vertex_indices_normalized) == 418
  426. assert vertex_indices_normalized[66:73] == [
  427. 61044,
  428. 62119,
  429. 62118,
  430. 62107,
  431. 62118,
  432. 63255,
  433. 63264,
  434. ]
  435. assert vertex_indices_normalized[:4] == [32065, 32072, 32073, 32080]
  436. assert vertex_indices_normalized[-4:] == [36281, 34870, 33454, 33450]
  437. def test_find_label_border_polygonal_chains_long_leaf():
  438. surface = Surface()
  439. with unittest.mock.patch.object(
  440. surface,
  441. "_find_label_border_segments",
  442. return_value=[
  443. LineSegment((0, 1)),
  444. LineSegment((1, 2)),
  445. LineSegment((0, 3)),
  446. LineSegment((2, 3)),
  447. LineSegment((2, 4)),
  448. LineSegment((4, 5)), # leaf
  449. ],
  450. ):
  451. (border_chain,) = surface.find_label_border_polygonal_chains("dummy")
  452. assert list(border_chain.normalized().vertex_indices) == [0, 1, 2, 4, 5, 4, 2, 3]
  453. def test__unused_vertices():
  454. surface = Surface()
  455. assert not surface._unused_vertices()
  456. for i in range(4):
  457. surface.add_vertex(Vertex(i, i, i))
  458. assert surface._unused_vertices() == {0, 1, 2, 3}
  459. surface.triangles.append(Triangle((0, 2, 3)))
  460. assert surface._unused_vertices() == {1}
  461. surface.triangles.append(Triangle((0, 3, 1)))
  462. assert not surface._unused_vertices()
  463. del surface.triangles[0]
  464. assert surface._unused_vertices() == {2}
  465. def test__unused_vertices_real():
  466. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  467. assert not surface._unused_vertices()
  468. surface.triangles = list(
  469. filter(lambda t: 42 not in t.vertex_indices, surface.triangles)
  470. )
  471. assert surface._unused_vertices() == {42}
  472. def test_remove_unused_vertices_all():
  473. surface = Surface()
  474. for i in range(5):
  475. surface.add_vertex(Vertex(i, i, i))
  476. assert len(surface.vertices) == 5
  477. surface.remove_unused_vertices()
  478. assert not surface.vertices
  479. surface.remove_unused_vertices()
  480. assert not surface.vertices
  481. def test_remove_unused_vertices_almost_all():
  482. surface = Surface()
  483. for i in range(5):
  484. surface.add_vertex(Vertex(i, i, i))
  485. assert len(surface.vertices) == 5
  486. surface.triangles.append(Triangle((0, 2, 3)))
  487. surface.remove_unused_vertices()
  488. assert len(surface.vertices) == 3
  489. assert surface.vertices[0] == pytest.approx(Vertex(0, 0, 0))
  490. assert surface.vertices[1] == pytest.approx(Vertex(2, 2, 2))
  491. assert surface.vertices[2] == pytest.approx(Vertex(3, 3, 3))
  492. assert len(surface.triangles) == 1
  493. assert surface.triangles[0] == Triangle((0, 1, 2))
  494. del surface.triangles[0]
  495. surface.remove_unused_vertices()
  496. assert not surface.vertices
  497. def test_remove_unused_vertices_some():
  498. surface = Surface()
  499. for i in range(9):
  500. surface.add_vertex(Vertex(i, i, i))
  501. surface.triangles.append(Triangle((0, 2, 3)))
  502. surface.triangles.append(Triangle((3, 4, 5)))
  503. surface.triangles.append(Triangle((3, 2, 5)))
  504. surface.triangles.append(Triangle((3, 2, 8)))
  505. surface.remove_unused_vertices()
  506. assert len(surface.vertices) == 6
  507. assert surface.vertices[0] == pytest.approx(Vertex(0, 0, 0))
  508. assert surface.vertices[1] == pytest.approx(Vertex(2, 2, 2))
  509. assert surface.vertices[2] == pytest.approx(Vertex(3, 3, 3))
  510. assert surface.vertices[3] == pytest.approx(Vertex(4, 4, 4))
  511. assert surface.vertices[4] == pytest.approx(Vertex(5, 5, 5))
  512. assert surface.vertices[5] == pytest.approx(Vertex(8, 8, 8))
  513. assert len(surface.triangles) == 4
  514. assert surface.triangles[0] == Triangle((0, 1, 2))
  515. assert surface.triangles[1] == Triangle((2, 3, 4))
  516. assert surface.triangles[2] == Triangle((2, 1, 4))
  517. assert surface.triangles[3] == Triangle((2, 1, 5))
  518. surface.remove_unused_vertices()
  519. assert len(surface.triangles) == 4
  520. def test_remove_unused_vertices_none():
  521. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  522. assert len(surface.vertices) == 155622
  523. assert len(surface.triangles) == 311240
  524. surface.remove_unused_vertices()
  525. assert len(surface.vertices) == 155622
  526. assert len(surface.triangles) == 311240
  527. def test_remove_unused_vertices_single():
  528. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  529. assert len(surface.vertices) == 155622
  530. assert len(surface.triangles) == 311240
  531. assert surface.triangles[-1] == Triangle((136143, 138007, 137078))
  532. surface.triangles = list(
  533. filter(lambda t: 42 not in t.vertex_indices, surface.triangles)
  534. )
  535. assert surface._unused_vertices() == {42}
  536. surface.remove_unused_vertices()
  537. assert len(surface.vertices) == 155622 - 1
  538. assert len(surface.triangles) == 311240 - 7
  539. assert surface.triangles[-1] == Triangle((136142, 138006, 137077))
  540. assert all(
  541. vertex_index < len(surface.vertices)
  542. for triangle in surface.triangles
  543. for vertex_index in triangle.vertex_indices
  544. )
  545. def test_select_vertices():
  546. surface = Surface()
  547. for i in range(4):
  548. surface.add_vertex(Vertex(i, i, i))
  549. assert numpy.allclose(
  550. surface.select_vertices([2, 1]), [surface.vertices[2], surface.vertices[1]]
  551. )
  552. assert numpy.allclose(
  553. surface.select_vertices([3, 2]), [surface.vertices[3], surface.vertices[2]]
  554. )
  555. assert numpy.allclose(surface.select_vertices((3, 2)), [[3, 3, 3], [2, 2, 2]])
  556. assert numpy.allclose(
  557. surface.select_vertices(filter(lambda i: i % 2 == 1, range(4))),
  558. [[1, 1, 1], [3, 3, 3]],
  559. )
  560. def test_unite_2():
  561. surface_a = Surface()
  562. for i in range(0, 4):
  563. surface_a.add_vertex(Vertex(i, i, i))
  564. surface_a.triangles.append(Triangle((0, 1, 2)))
  565. surface_a.triangles.append(Triangle((1, 2, 3)))
  566. surface_b = Surface()
  567. for i in range(10, 14):
  568. surface_b.add_vertex(Vertex(i, i, i))
  569. surface_b.triangles.append(Triangle((0, 1, 3)))
  570. surface_a_copy = copy.deepcopy(surface_a)
  571. surface_b_copy = copy.deepcopy(surface_b)
  572. union = Surface.unite([surface_a, surface_b])
  573. assert numpy.allclose(surface_a.vertices, surface_a_copy.vertices)
  574. assert numpy.allclose(surface_b.vertices, surface_b_copy.vertices)
  575. assert numpy.allclose(union.vertices[:4], surface_a.vertices)
  576. assert numpy.allclose(union.vertices[4:], surface_b.vertices)
  577. assert surface_a.triangles == surface_a_copy.triangles
  578. assert surface_b.triangles == surface_b_copy.triangles
  579. assert union.triangles[:2] == surface_a.triangles
  580. assert union.triangles[2:] == [Triangle((4, 5, 7))]
  581. def test_unite_3():
  582. surface_a = Surface()
  583. for i in range(0, 4):
  584. surface_a.add_vertex(Vertex(i, i, i))
  585. surface_a.triangles.append(Triangle((0, 1, 2)))
  586. surface_a.triangles.append(Triangle((1, 2, 3)))
  587. surface_b = Surface()
  588. for i in range(10, 14):
  589. surface_b.add_vertex(Vertex(i, i, i))
  590. surface_b.triangles.append(Triangle((0, 1, 3)))
  591. surface_c = Surface()
  592. for i in range(20, 23):
  593. surface_c.add_vertex(Vertex(i, i, i))
  594. surface_c.triangles.append(Triangle((0, 1, 2)))
  595. surface_c.triangles.append(Triangle((0, 1, 2)))
  596. union = Surface.unite(filter(None, [surface_a, surface_b, surface_c]))
  597. assert numpy.allclose(union.vertices[:4], surface_a.vertices)
  598. assert numpy.allclose(union.vertices[4:8], surface_b.vertices)
  599. assert numpy.allclose(union.vertices[8:], surface_c.vertices)
  600. assert union.triangles[:2] == surface_a.triangles
  601. assert union.triangles[2:3] == [Triangle((4, 5, 7))]
  602. assert union.triangles[3:] == [Triangle((8, 9, 10)), Triangle((8, 9, 10))]
  603. def test_unite_real():
  604. surface_a = Surface.read_triangular(SURFACE_FILE_PATH)
  605. surface_b = Surface()
  606. for i in range(5):
  607. surface_b.add_vertex(Vertex(i, i, i))
  608. surface_b.triangles.append(Triangle((0, 1, 3)))
  609. surface_b.triangles.append(Triangle((1, 3, 4)))
  610. union = Surface.unite((surface_a, surface_b))
  611. assert numpy.allclose(union.vertices[:-5], surface_a.vertices)
  612. assert numpy.allclose(union.vertices[-5:], surface_b.vertices)
  613. assert union.triangles[:-2] == surface_a.triangles
  614. assert union.triangles[-2:] == [
  615. Triangle((155622, 155623, 155625)),
  616. Triangle((155623, 155625, 155626)),
  617. ]
  618. assert union.creator == surface_a.creator
  619. assert union.creation_datetime == surface_a.creation_datetime
  620. assert union.using_old_real_ras == surface_a.using_old_real_ras
  621. assert union.volume_geometry_info == surface_a.volume_geometry_info
  622. assert union.command_lines == surface_a.command_lines
  623. assert union.annotation == surface_a.annotation