test_surface.py 23 KB

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