mtgeodesicdome 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2022-2026 Masahiro Takatsuka. See the NOTICE file for attribution terms.
3
+ """
4
+ mt.geodesicdome -- geodesic domes, grids and map projections.
5
+
6
+ Subpackages
7
+ mt.geodesicdome.grid GeodesicDome (icosahedron-based sphere) and Plane grids
8
+ mt.geodesicdome.projection map projections (Equal Earth, Kavrayskiy VII, Wagner VI/III)
9
+ mt.geodesicdome.interactive rotatable interactive map viewer (needs matplotlib)
10
+
11
+ The indexed geodesic data structure is described in
12
+ Y. Wu and M. Takatsuka, "Spherical self-organizing map using efficient indexed
13
+ geodesic data structure", Neural Networks 19(6-7):900-910, 2006.
14
+ doi:10.1016/j.neunet.2006.05.021
15
+
16
+ Note: `mt` itself is a namespace package (no __init__.py), so other
17
+ mt.* distributions can be installed alongside this one.
18
+ """
19
+
20
+ __version__ = '1.2.0'
@@ -0,0 +1,3 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2022-2026 Masahiro Takatsuka. See the NOTICE file for attribution terms.
3
+ """Lattices: GeodesicDome (geodesicdome.py) and Plane (plane.py)."""
@@ -0,0 +1,563 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2022-2026 Masahiro Takatsuka. See the NOTICE file for attribution terms.
3
+
4
+ import numpy as np
5
+ from numpy import array
6
+
7
+ from mt.geodesicdome import util
8
+ from mt.geodesicdome.manifold import Manifold
9
+ from mt.geodesicdome.vertex import Vertex
10
+
11
+
12
+ class IGeodesicDome:
13
+ pass
14
+
15
+
16
+ class GeodesicVertex(Vertex):
17
+ def __init__(self, latitude=None, longitude=None, coord=None, x=None, y=None, frequency=1):
18
+ super().__init__(x, y)
19
+ self.vertices: list[list[GeodesicVertex]] = [[]]
20
+ self.same_vertices: list[GeodesicVertex] = None
21
+ self.triangles: list[GeodesicVertex] = []
22
+ self.frequency = frequency
23
+ self.coord: array
24
+ self.projected_coord: array
25
+ self.latlon_coord: array
26
+ if (latitude is not None) and (longitude is not None):
27
+ self.coord = util.spherical_to_xyz(latitude, longitude)
28
+ self.latlon_coord = np.array([latitude, longitude])
29
+ elif coord.any():
30
+ self.coord = coord
31
+ self.latlon_coord = util.xyz_to_spherical(coord)
32
+
33
+
34
+ def _mark_same_vertices(v: GeodesicVertex, visit_or_not: bool) -> None:
35
+ """
36
+ This function sets the same visited status to all 'same' vertices.
37
+
38
+ :type v: GeodesicVertex
39
+ :param v: The vertex whose registered same vertices will have the specified visited status.
40
+ :type visit_or_not: bool
41
+ :param visit_or_not: the new visited or not status.
42
+ :return: None
43
+ """
44
+ if v.same_vertices is not None and v.same_vertices:
45
+ for vSame in v.same_vertices:
46
+ vSame.visited = visit_or_not
47
+
48
+
49
+ def _partition(frequency: int, v1: GeodesicVertex, v2: GeodesicVertex) -> list[GeodesicVertex]:
50
+ """
51
+ This function will generate (frequency - 1) new vertices between v1 and v2 so that
52
+ the edge (v1, v2) are equally divided.
53
+
54
+ :param frequency: The frequency of the Geodesicdome.
55
+ For example, frequency 2 will divide an edge into two edges by inserting one vertex.
56
+ :param v1: One of two vertices defining an edge.
57
+ :param v2: One of two vertices defining an edge.
58
+ :return: A list of newly created (incerted) vertices.
59
+ """
60
+ dx = (v2.coord[0] - v1.coord[0]) / frequency
61
+ dy = (v2.coord[1] - v1.coord[1]) / frequency
62
+ dz = (v2.coord[2] - v1.coord[2]) / frequency
63
+ new_vertices: list[GeodesicVertex] = [None] * (frequency - 1) # GeodesicVertex[frequency - 1];
64
+ dxIndex = int((v2.x - v1.x) / frequency)
65
+ dyIndex = int((v2.y - v1.y) / frequency)
66
+ # print(f'(dxIndex, dyIndex): {dxIndex}, {dyIndex}')
67
+ for j in range(1, frequency):
68
+ new_coord = np.array([v1.coord[0] + j * dx, v1.coord[1] + j * dy, v1.coord[2] + j * dz])
69
+ new_coord = new_coord / np.linalg.norm(new_coord)
70
+ x = v1.x + dxIndex * j
71
+ y = v1.y + dyIndex * j
72
+ # print(f'(x, y): {x}, {y}')
73
+ new_vertices[j - 1] = GeodesicVertex(coord=new_coord, x=x, y=y, frequency=frequency)
74
+
75
+ return new_vertices
76
+
77
+
78
+ def _split_x_vertices(vertices_x: list[GeodesicVertex], frequency: int) -> list[GeodesicVertex]:
79
+ # new vector containing the new higher frequency vertex
80
+ vNum = len(vertices_x) + (frequency - 1) * (len(vertices_x) - 1)
81
+ new_vertices_x: list[GeodesicVertex] = [None] * vNum
82
+ v1: GeodesicVertex = vertices_x[0]
83
+
84
+ # these will shift x and y coordinates to make room for new vertices.
85
+ v1.x *= frequency
86
+ v1.y *= frequency
87
+ offset = v1.y
88
+
89
+ for j in range(1, len(vertices_x)):
90
+ # create the higher frequency vertex
91
+ v2: GeodesicVertex = vertices_x[j]
92
+ v2.x *= frequency
93
+ v2.y *= frequency
94
+
95
+ created: list[GeodesicVertex] = _partition(frequency, v1, v2)
96
+ new_vertices_x[v1.y - offset] = v1
97
+ for k in range(len(created)):
98
+ new_vertices_x[v1.y + k + 1 - offset] = created[k]
99
+ new_vertices_x[v1.y + frequency - offset] = v2
100
+ v1 = v2
101
+
102
+ return new_vertices_x
103
+
104
+
105
+ class GeodesicDome(IGeodesicDome, Manifold):
106
+ """
107
+ A Geodesicdome based on the Icosahedron (22 vertices and 20 triangles)
108
+ The base Icosahedron's vertices are arranged on a rectilinear grid as shown below.
109
+ This is to use an indexing scheme to quickly search vertex's i-level neighbours.
110
+
111
+ # v17 v20 v22
112
+ # v13 v16 v19 v21
113
+ # v9 v12 v15 v18
114
+ # v5 v8 v11 v14
115
+ # v2 v4 v7 v10
116
+ # v1 v3 v6
117
+ """
118
+
119
+ """
120
+ Icosahedron's approximated average arc length
121
+ """
122
+ _arc_length = 1.106588
123
+
124
+ def __init__(self, frequency=1):
125
+ super().__init__()
126
+ self.arcLength = GeodesicDome._arc_length # approximate the average arc length
127
+ # start from the base icosahedron (frequency 1); split() below multiplies this
128
+ self.frequency = 1
129
+ self.x_max = 6
130
+ self.y_max = 5
131
+ self.vertices: list[list[GeodesicVertex]] = [[]] * (self.x_max + 1)
132
+
133
+ # initialize the icosahedron, calculate all the vertex coordinates
134
+ dAzimuth = 2 * np.pi / 5
135
+ dLatitude = ((90 - 26.565) / 180) * np.pi
136
+
137
+ # set 22 vertices
138
+ v1 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, 0), x=0, y=0, frequency=1)
139
+ v2 = GeodesicVertex(coord=util.spherical_to_xyz(0, 0), x=0, y=1, frequency=1)
140
+ vList1 = [v1, v2]
141
+ self.vertices[0] = vList1
142
+
143
+ v3 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 0.5), x=1, y=0, frequency=1)
144
+ v4 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, dAzimuth), x=1, y=1, frequency=1)
145
+ v5 = GeodesicVertex(coord=util.spherical_to_xyz(0, 0), x=1, y=2, frequency=1)
146
+ vList2 = [v3, v4, v5]
147
+ self.vertices[1] = vList2
148
+
149
+ v6 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi, 0), x=2, y=0, frequency=1)
150
+ v7 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 1.5), x=2, y=1, frequency=1)
151
+ v8 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, dAzimuth * 2), x=2, y=2, frequency=1)
152
+ v9 = GeodesicVertex(coord=util.spherical_to_xyz(0, 0), x=2, y=3, frequency=1)
153
+ vList3 = [v6, v7, v8, v9]
154
+ self.vertices[2] = vList3
155
+
156
+ v10 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi, 0), x=3, y=1, frequency=1)
157
+ v11 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 2.5), x=3, y=2, frequency=1)
158
+ v12 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, dAzimuth * 3), x=3, y=3, frequency=1)
159
+ v13 = GeodesicVertex(coord=util.spherical_to_xyz(0, 0), x=3, y=4, frequency=1)
160
+ vList4 = [v10, v11, v12, v13]
161
+ self.vertices[3] = vList4
162
+
163
+ v14 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi, 0), x=4, y=2, frequency=1)
164
+ v15 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 3.5), x=4, y=3, frequency=1)
165
+ v16 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, dAzimuth * 4), x=4, y=4, frequency=1)
166
+ v17 = GeodesicVertex(coord=util.spherical_to_xyz(0, 0), x=4, y=5, frequency=1)
167
+ vList5 = [v14, v15, v16, v17]
168
+ self.vertices[4] = vList5
169
+
170
+ v18 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi, 0), x=5, y=3, frequency=1)
171
+ v19 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 4.5), x=5, y=4, frequency=1)
172
+ v20 = GeodesicVertex(coord=util.spherical_to_xyz(dLatitude, 0), x=5, y=5, frequency=1)
173
+ vList6 = [v18, v19, v20]
174
+ self.vertices[5] = vList6
175
+
176
+ v21 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi, 0), x=6, y=4, frequency=1)
177
+ v22 = GeodesicVertex(coord=util.spherical_to_xyz(np.pi - dLatitude, dAzimuth * 0.5), x=6, y=5, frequency=1)
178
+ vList7 = [v21, v22]
179
+ self.vertices[6] = vList7
180
+
181
+ # initialize same vertices list for vertices
182
+ # v1
183
+ v1.same_vertices = [v20]
184
+
185
+ # v2
186
+ v2.same_vertices = [v5, v9, v13, v17]
187
+
188
+ # v3
189
+ v3.same_vertices = [v22]
190
+
191
+ # v5
192
+ v5.same_vertices = [v2, v9, v13, v17]
193
+
194
+ # v6
195
+ v6.same_vertices = [v10, v14, v18, v21]
196
+
197
+ # v9
198
+ v9.same_vertices = [v2, v5, v13, v17]
199
+
200
+ # v10
201
+ v10.same_vertices = [v6, v14, v18, v21]
202
+
203
+ # v13
204
+ v13.same_vertices = [v2, v5, v9, v17]
205
+
206
+ # v14
207
+ v14.same_vertices = [v6, v10, v18, v21]
208
+
209
+ # v17
210
+ v17.same_vertices = [v2, v5, v9, v13]
211
+
212
+ # v18
213
+ v18.same_vertices = [v6, v10, v14, v21]
214
+
215
+ # v20
216
+ v20.same_vertices = [v1]
217
+
218
+ # v21
219
+ v21.same_vertices = [v6, v10, v14, v18]
220
+
221
+ # v22
222
+ v22.same_vertices = [v3]
223
+
224
+ if frequency > 1:
225
+ self.split(frequency)
226
+
227
+ def __repr__(self) -> str:
228
+ return f'GeodesicDome(frequency={self.frequency})'
229
+
230
+ def _find_same_vertices(self):
231
+ """
232
+ used after split, find the same vertices for the new generated vertices
233
+ *
234
+ / |
235
+ / /*
236
+ / / |
237
+ ^ *--*--*
238
+ | |
239
+ | *
240
+ |
241
+ """
242
+ first_x: list[GeodesicVertex] = self.vertices[0]
243
+ top_current: GeodesicVertex = first_x[len(first_x) - 1]
244
+ top_next: GeodesicVertex = None
245
+ # stage1, travel through the top vertices that has 4 same vertices points
246
+ # through v2, v5, v9, v13 and v17
247
+ i = 0
248
+ while i < len(top_current.same_vertices):
249
+ top_next = top_current.same_vertices[i]
250
+ x1 = top_current.x
251
+ y1 = top_current.y
252
+ x2 = top_next.x
253
+ y2 = top_next.y
254
+ top_middle: GeodesicVertex = self.get_vertex_at(x1 + 1, y1)
255
+ while (x1 + 1) != x2:
256
+ if top_middle.same_vertices is not None and top_middle.same_vertices:
257
+ x1 += 1
258
+ y2 -= 1
259
+ top_middle = self.get_vertex_at(x1 + 1, y1)
260
+ continue
261
+
262
+ top_middle_match = self.get_vertex_at(x2, y2 - 1)
263
+ top_middle.same_vertices = [top_middle_match]
264
+ top_middle_match.same_vertices = [top_middle]
265
+
266
+ x1 += 1
267
+ y2 -= 1
268
+ top_middle = self.get_vertex_at(x1 + 1, y1)
269
+
270
+ top_current = top_next
271
+ i += 1
272
+
273
+ # stage 2, travel through the flat top vertex
274
+ # through v20, v21
275
+ v17_x = top_next.x # v17
276
+ v17_y = top_next.y
277
+ v2 = self.vertices[0][len(self.vertices[0]) - 1]
278
+ for i in range(v17_x + 1, self.x_max):
279
+ vHigh = self.get_vertex_at(i, v17_y)
280
+ if vHigh.same_vertices is not None and vHigh.same_vertices:
281
+ continue
282
+
283
+ xdiff = vHigh.x - v17_x
284
+ vLow = self.get_vertex_at(0, v2.y - xdiff) if (xdiff < v2.y) else self.get_vertex_at(xdiff - v2.y, 0)
285
+
286
+ vHigh.same_vertices = [vLow]
287
+ vLow.same_vertices = [vHigh]
288
+
289
+ # through v21 to v22
290
+ last_x = self.vertices[self.x_max]
291
+ v22 = last_x[len(last_x) - 1]
292
+ v3 = v22.same_vertices[0]
293
+ for i in range(len(last_x) - 1 - 1, 0, -1):
294
+ test = last_x[i]
295
+ if test.same_vertices is not None and test.same_vertices:
296
+ continue
297
+ match = self.get_vertex_at(v3.x + (v22.y - test.y), 0)
298
+ match.same_vertices = [test]
299
+ test.same_vertices = [match]
300
+
301
+ # stage 3, travel through the boottom vertice that has 4 same vertices
302
+ # through v21, v18, v14, v10, v6
303
+ v21 = last_x[0]
304
+ v1 = v21
305
+ i = len(v1.same_vertices) - 1
306
+ while i >= 0:
307
+ v2 = v1.same_vertices[i] # v18
308
+ x1 = v1.x
309
+ y1 = v1.y
310
+ x2 = v2.x
311
+ y2 = v2.y
312
+ v3: GeodesicVertex = self.get_vertex_at(x1 - 1, y1)
313
+ while (x1 - 1) != x2:
314
+ # print(f'x1, x2, y1, y2, v2, v3 = {x1, x2, y1, y2, v2, v3}')
315
+ if v3.same_vertices is not None and v3.same_vertices:
316
+ x1 -= 1
317
+ y2 += 1
318
+ v3 = self.get_vertex_at(x1 - 1, y1)
319
+ continue
320
+
321
+ v4 = self.get_vertex_at(x2, y2 + 1)
322
+ v3.same_vertices = [v4]
323
+ v4.same_vertices = [v3]
324
+
325
+ x1 -= 1
326
+ y2 += 1
327
+ v3 = self.get_vertex_at(x1 - 1, y1)
328
+
329
+ v1 = v2
330
+ i -= 1
331
+
332
+ # increase frequency
333
+ def split(self, frequency):
334
+ """
335
+ Subdivides every edge of the current dome into `frequency` segments.
336
+ Calls are cumulative: split(2) followed by split(3) gives frequency 6.
337
+
338
+ :param frequency: subdivision factor (an int >= 2)
339
+ :return: None
340
+ """
341
+ self.frequency *= frequency
342
+ self.x_max *= frequency
343
+ self.y_max *= frequency
344
+ self.arcLength /= frequency
345
+
346
+ # temporary new vector for storing the vertices of the new frequency
347
+ new_vertices: list[list[GeodesicVertex]] = [[]] * (self.x_max + 1)
348
+
349
+ # split the first vector of x-vertices
350
+ new_x_vertices1: list[GeodesicVertex] = _split_x_vertices(self.vertices[0], frequency)
351
+ new_vertices[0] = new_x_vertices1
352
+ # print(f'# of columns : {len(self.vertices)}')
353
+ for i in range(len(self.vertices) - 1):
354
+ # print(f'processing index : {i}')
355
+ new_x_vertices2: list[GeodesicVertex] = _split_x_vertices(self.vertices[i + 1], frequency)
356
+ new_vertices[(i + 1) * frequency] = new_x_vertices2
357
+
358
+ # create the vertices between the 2 new vectors
359
+ RB = new_x_vertices2[0] # right bottom
360
+ LT = new_x_vertices1[len(new_x_vertices1) - 1] # left top
361
+ length = (LT.y - RB.y + 1)
362
+
363
+ # prepare (x-vertices * (frequency -1)) to be inserted
364
+ for k in range(frequency - 1):
365
+ index: int = i * frequency + k + 1
366
+ new_vertices[index] = [None] * length
367
+
368
+ # fill-in key horizontals.
369
+ horiindex = []
370
+ for n in range(len(new_x_vertices1) - 1, -1, -frequency):
371
+ left: GeodesicVertex = new_x_vertices1[n]
372
+ # print(f'left.y: {left.y}')
373
+ right: GeodesicVertex = None
374
+ right_y: int = 0
375
+ for m in range(len(new_x_vertices2) - 1, -1, -1):
376
+ testing: GeodesicVertex = new_x_vertices2[m]
377
+ # print(f'testing: {testing.y}')
378
+ if testing.y == left.y:
379
+ right = testing
380
+ right_y = m
381
+ horiindex.append([n, m])
382
+ break
383
+ if right is not None:
384
+ inserting: list[GeodesicVertex] = _partition(frequency, left, right)
385
+ for v in inserting:
386
+ new_vertices[v.x][right_y] = v
387
+
388
+ # fill-in diagonals
389
+ base_index = i * frequency
390
+ for hi in range(len(horiindex) - 1):
391
+ [h_index, m] = horiindex[hi]
392
+ for h_count in range(2, frequency + 1):
393
+ b1 = new_vertices[base_index][h_index - h_count]
394
+ t1h = len(new_vertices[base_index + h_count]) - 1 - hi * frequency if h_count < frequency else m
395
+ t1 = new_vertices[base_index + h_count][t1h]
396
+ new_points1 = _partition(h_count, b1, t1)
397
+ for ii in range(len(new_points1)):
398
+ new_v = new_points1[ii]
399
+ new_vertices[new_v.x][t1h - h_count + 1 + ii] = new_v
400
+ if h_count < frequency:
401
+ b2 = new_vertices[base_index + frequency - h_count][t1h - frequency]
402
+ t2 = new_vertices[base_index + frequency][m - frequency + h_count]
403
+ new_points2 = _partition(h_count, b2, t2)
404
+ for jj in range(len(new_points2)):
405
+ new_v = new_points2[jj]
406
+ new_vertices[new_v.x][t1h - frequency + 1 + jj] = new_v
407
+
408
+ # ready for the next iteration
409
+ new_x_vertices1 = new_x_vertices2
410
+
411
+ self.vertices = new_vertices
412
+ self._find_same_vertices()
413
+
414
+ def get_all_vertices(self) -> list[GeodesicVertex]:
415
+ all_vertices: list[GeodesicVertex] = []
416
+ for row in self.vertices:
417
+ all_vertices.extend(row)
418
+ return all_vertices
419
+
420
+ def get_vertex_at(self, x, y) -> GeodesicVertex:
421
+ if (x <= self.x_max) and (y <= self.y_max) and (x >= 0) and (y >= 0):
422
+ xVector: list[GeodesicVertex] = self.vertices[x]
423
+
424
+ # offset
425
+ offset = xVector[0].y
426
+ if y >= offset and y < (len(xVector) + offset):
427
+ return xVector[y - offset]
428
+
429
+ def get_number_of_vertices_per_face(self) -> int:
430
+ return 3
431
+
432
+ def _updateIDs(self) -> None:
433
+ serial_number = 0
434
+ for v in self.get_all_vertices():
435
+ v.id = serial_number
436
+ serial_number += 1
437
+
438
+ def _unmark_vertices(self) -> None:
439
+ for x_list in self.vertices:
440
+ for v in x_list:
441
+ v.visited = False
442
+
443
+ def _build_faces(self) -> list[GeodesicVertex]:
444
+ self._updateIDs()
445
+ vNum = 3 * 20 * self.frequency * self.frequency
446
+ self.triangles: list[GeodesicVertex] = [None] * vNum
447
+ index = 0
448
+ for i in range(len(self.vertices) - 1):
449
+ # print(f'builsing face at : {i}')
450
+ current_x: list[GeodesicVertex] = self.vertices[i]
451
+ next_x: list[GeodesicVertex] = self.vertices[i + 1]
452
+ next_bottom_y = next_x[0].y
453
+ for j in range(len(current_x) - 1):
454
+ if current_x[j].y == next_bottom_y: # found the starting point
455
+ for k in range(j, len(current_x) - 1):
456
+ # triangle 1
457
+ self.triangles[index] = current_x[k] # v1
458
+ self.triangles[index + 1] = next_x[k - j] # v2
459
+ self.triangles[index + 2] = next_x[k - j + 1] # v3
460
+
461
+ # triangle 2
462
+ self.triangles[index + 3] = self.triangles[index]
463
+ self.triangles[index + 4] = self.triangles[index + 2]
464
+ self.triangles[index + 5] = current_x[k + 1] # v4
465
+ index += 6
466
+ break
467
+
468
+ return self.triangles
469
+
470
+ def get_faces(self) -> list[GeodesicVertex]:
471
+ self._build_faces()
472
+ return self.triangles
473
+
474
+ def get_neighbours(self, v: GeodesicVertex, visit_same_vertex: bool) -> list[GeodesicVertex]:
475
+ v.visited = True
476
+ x = v.x
477
+ y = v.y
478
+ if v.same_vertices:
479
+ _mark_same_vertices(v, True)
480
+
481
+ # find the neighbors
482
+ neighbours: list[GeodesicVertex] = []
483
+ xArray1 = self.vertices[v.x]
484
+ offset1 = xArray1[0].y
485
+
486
+ # x, y + 1
487
+ if (y + 1) < (len(xArray1) + offset1):
488
+ n1 = xArray1[y + 1 - offset1]
489
+ if not n1.visited:
490
+ n1.visited = True
491
+ neighbours.append(n1)
492
+ if n1.same_vertices:
493
+ _mark_same_vertices(n1, True)
494
+ # x, y - 1
495
+ if (y - 1) >= offset1:
496
+ n4 = xArray1[y - 1 - offset1]
497
+ if not n4.visited:
498
+ n4.visited = True
499
+ neighbours.append(n4)
500
+ if n4.same_vertices:
501
+ _mark_same_vertices(n4, True)
502
+
503
+ if (x + 1) <= self.x_max:
504
+ xArray2 = self.vertices[v.x + 1]
505
+ offset2 = xArray2[0].y
506
+
507
+ # x+1, y
508
+ if y >= offset2 and y < (len(xArray2) + offset2):
509
+ n2 = xArray2[y - offset2]
510
+ if not n2.visited:
511
+ n2.visited = True
512
+ neighbours.append(n2)
513
+ if n2.same_vertices:
514
+ _mark_same_vertices(n2, True)
515
+
516
+ '''
517
+ x+1, y+1 is most probably v's same vertex point
518
+ if n3's is in v.sameVertics, markSameVertex(n3,frequency) would mark all
519
+ the v's same vertices, so that the program would not searching for v's same Vertices's
520
+ neighbor later
521
+ '''
522
+ if (y + 1) >= offset2 and (y + 1) < len(xArray2) + offset2:
523
+ n3 = xArray2[y + 1 - offset2]
524
+ if not n3.visited:
525
+ n3.visited = True
526
+ neighbours.append(n3)
527
+ if n3.same_vertices:
528
+ _mark_same_vertices(n3, True)
529
+ if (x - 1) >= 0:
530
+ xArray3 = self.vertices[v.x - 1]
531
+ offset3 = xArray3[0].y
532
+ '''
533
+ x - 1, y - 1 is most probably v's same vertex point
534
+ if n5's is in v.sameVerticsList, markSameVertex(n5,frequency) would mark all
535
+ the v's same vertices, so that the program would not searching for v's same Vertices's
536
+ neighbor later
537
+ '''
538
+ if (y - 1) < (len(xArray3) + offset3) and (y - 1) >= offset3:
539
+ n5 = xArray3[y - 1 - offset3]
540
+ if not n5.visited:
541
+ n5.visited = True
542
+ neighbours.append(n5)
543
+ if n5.same_vertices:
544
+ _mark_same_vertices(n5, True)
545
+
546
+ # x - 1, y
547
+ if y < (len(xArray3) + offset3) and y >= offset3:
548
+ n6: GeodesicVertex = xArray3[y - offset3]
549
+ if not n6.visited:
550
+ n6.visited = True
551
+ neighbours.append(n6)
552
+ if n6.same_vertices:
553
+ _mark_same_vertices(n6, True)
554
+
555
+ # find the neighbor of the same vertex;
556
+ if v.same_vertices is None or visit_same_vertex:
557
+ return neighbours
558
+
559
+ for vSame in v.same_vertices:
560
+ vSameNeighbor = self.get_neighbours(vSame, True)
561
+ neighbours.extend(vSameNeighbor)
562
+
563
+ return neighbours