test_surface.py 26 KB

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