3
3
from collections .abc import Iterable , Sized
4
4
from itertools import chain , combinations
5
5
from math import factorial
6
+ from typing import Any , Iterator , List , Optional , Set , Tuple , Union
6
7
7
8
import numpy as np
8
9
import scipy .spatial
10
+ from numpy import bool_ , float64 , int32 , ndarray
9
11
10
12
11
- def fast_norm (v ) :
13
+ def fast_norm (v : Union [ Tuple [ float64 , float64 , float64 ], ndarray ]) -> float :
12
14
# notice this method can be even more optimised
13
15
if len (v ) == 2 :
14
16
return math .sqrt (v [0 ] * v [0 ] + v [1 ] * v [1 ])
@@ -17,7 +19,15 @@ def fast_norm(v):
17
19
return math .sqrt (np .dot (v , v ))
18
20
19
21
20
- def fast_2d_point_in_simplex (point , simplex , eps = 1e-8 ):
22
+ def fast_2d_point_in_simplex (
23
+ point : Union [Tuple [int , int ], Tuple [float , float ], Tuple [float64 , float64 ]],
24
+ simplex : Union [
25
+ List [Union [Tuple [int , int ], Tuple [float64 , float64 ]]],
26
+ List [Tuple [float64 , float64 ]],
27
+ ndarray ,
28
+ ],
29
+ eps : float = 1e-8 ,
30
+ ) -> Union [bool , bool_ ]:
21
31
(p0x , p0y ), (p1x , p1y ), (p2x , p2y ) = simplex
22
32
px , py = point
23
33
@@ -31,7 +41,7 @@ def fast_2d_point_in_simplex(point, simplex, eps=1e-8):
31
41
return (t >= - eps ) and (s + t <= 1 + eps )
32
42
33
43
34
- def point_in_simplex (point , simplex , eps = 1e-8 ):
44
+ def point_in_simplex (point : Any , simplex : Any , eps : float = 1e-8 ) -> Union [ bool , bool_ ] :
35
45
if len (point ) == 2 :
36
46
return fast_2d_point_in_simplex (point , simplex , eps )
37
47
@@ -42,7 +52,7 @@ def point_in_simplex(point, simplex, eps=1e-8):
42
52
return all (alpha > - eps ) and sum (alpha ) < 1 + eps
43
53
44
54
45
- def fast_2d_circumcircle (points ) :
55
+ def fast_2d_circumcircle (points : ndarray ) -> Tuple [ Tuple [ float64 , float64 ], float ] :
46
56
"""Compute the center and radius of the circumscribed circle of a triangle
47
57
48
58
Parameters
@@ -78,7 +88,9 @@ def fast_2d_circumcircle(points):
78
88
return (x + points [0 ][0 ], y + points [0 ][1 ]), radius
79
89
80
90
81
- def fast_3d_circumcircle (points ):
91
+ def fast_3d_circumcircle (
92
+ points : ndarray ,
93
+ ) -> Tuple [Tuple [float64 , float64 , float64 ], float ]:
82
94
"""Compute the center and radius of the circumscribed shpere of a simplex.
83
95
84
96
Parameters
@@ -118,7 +130,7 @@ def fast_3d_circumcircle(points):
118
130
return center , radius
119
131
120
132
121
- def fast_det (matrix ) :
133
+ def fast_det (matrix : ndarray ) -> float64 :
122
134
matrix = np .asarray (matrix , dtype = float )
123
135
if matrix .shape == (2 , 2 ):
124
136
return matrix [0 ][0 ] * matrix [1 ][1 ] - matrix [1 ][0 ] * matrix [0 ][1 ]
@@ -129,7 +141,13 @@ def fast_det(matrix):
129
141
return np .linalg .det (matrix )
130
142
131
143
132
- def circumsphere (pts ):
144
+ def circumsphere (
145
+ pts : ndarray ,
146
+ ) -> Union [
147
+ Tuple [Tuple [float64 , float64 , float64 , float64 ], float ],
148
+ Tuple [Tuple [float64 , float64 ], float ],
149
+ Tuple [Tuple [float64 , float64 , float64 ], float ],
150
+ ]:
133
151
dim = len (pts ) - 1
134
152
if dim == 2 :
135
153
return fast_2d_circumcircle (pts )
@@ -155,7 +173,7 @@ def circumsphere(pts):
155
173
return tuple (center ), radius
156
174
157
175
158
- def orientation (face , origin ) :
176
+ def orientation (face : Any , origin : Any ) -> Union [ int , float64 ] :
159
177
"""Compute the orientation of the face with respect to a point, origin.
160
178
161
179
Parameters
@@ -181,11 +199,18 @@ def orientation(face, origin):
181
199
return sign
182
200
183
201
184
- def is_iterable_and_sized (obj ) :
202
+ def is_iterable_and_sized (obj : Any ) -> bool :
185
203
return isinstance (obj , Iterable ) and isinstance (obj , Sized )
186
204
187
205
188
- def simplex_volume_in_embedding (vertices ) -> float :
206
+ def simplex_volume_in_embedding (
207
+ vertices : Union [
208
+ List [Tuple [float64 , float64 , float64 ]],
209
+ List [Tuple [float , float64 , float64 ]],
210
+ List [Tuple [float64 , float64 , float64 , float64 ]],
211
+ List [Tuple [float64 , float64 , float64 , float64 , float64 ]],
212
+ ]
213
+ ) -> float :
189
214
"""Calculate the volume of a simplex in a higher dimensional embedding.
190
215
That is: dim > len(vertices) - 1. For example if you would like to know the
191
216
surface area of a triangle in a 3d space.
@@ -266,7 +291,7 @@ class Triangulation:
266
291
or more simplices in the
267
292
"""
268
293
269
- def __init__ (self , coords ) :
294
+ def __init__ (self , coords : Any ) -> None :
270
295
if not is_iterable_and_sized (coords ):
271
296
raise TypeError ("Please provide a 2-dimensional list of points" )
272
297
coords = list (coords )
@@ -305,27 +330,27 @@ def __init__(self, coords):
305
330
for simplex in initial_tri .simplices :
306
331
self .add_simplex (simplex )
307
332
308
- def delete_simplex (self , simplex ) :
333
+ def delete_simplex (self , simplex : Any ) -> None :
309
334
simplex = tuple (sorted (simplex ))
310
335
self .simplices .remove (simplex )
311
336
for vertex in simplex :
312
337
self .vertex_to_simplices [vertex ].remove (simplex )
313
338
314
- def add_simplex (self , simplex ) :
339
+ def add_simplex (self , simplex : Any ) -> None :
315
340
simplex = tuple (sorted (simplex ))
316
341
self .simplices .add (simplex )
317
342
for vertex in simplex :
318
343
self .vertex_to_simplices [vertex ].add (simplex )
319
344
320
- def get_vertices (self , indices ) :
345
+ def get_vertices (self , indices : Any ) -> Any :
321
346
return [self .get_vertex (i ) for i in indices ]
322
347
323
- def get_vertex (self , index ) :
348
+ def get_vertex (self , index : Optional [ Union [ int32 , int ]]) -> Any :
324
349
if index is None :
325
350
return None
326
351
return self .vertices [index ]
327
352
328
- def get_reduced_simplex (self , point , simplex , eps = 1e-8 ) -> list :
353
+ def get_reduced_simplex (self , point : Any , simplex : Any , eps : float = 1e-8 ) -> list :
329
354
"""Check whether vertex lies within a simplex.
330
355
331
356
Returns
@@ -350,11 +375,13 @@ def get_reduced_simplex(self, point, simplex, eps=1e-8) -> list:
350
375
351
376
return [simplex [i ] for i in result ]
352
377
353
- def point_in_simplex (self , point , simplex , eps = 1e-8 ):
378
+ def point_in_simplex (
379
+ self , point : Any , simplex : Any , eps : float = 1e-8
380
+ ) -> Union [bool , bool_ ]:
354
381
vertices = self .get_vertices (simplex )
355
382
return point_in_simplex (point , vertices , eps )
356
383
357
- def locate_point (self , point ) :
384
+ def locate_point (self , point : Any ) -> Any :
358
385
"""Find to which simplex the point belongs.
359
386
360
387
Return indices of the simplex containing the point.
@@ -366,10 +393,12 @@ def locate_point(self, point):
366
393
return ()
367
394
368
395
@property
369
- def dim (self ):
396
+ def dim (self ) -> int :
370
397
return len (self .vertices [0 ])
371
398
372
- def faces (self , dim = None , simplices = None , vertices = None ):
399
+ def faces (
400
+ self , dim : None = None , simplices : Optional [Any ] = None , vertices : None = None
401
+ ) -> Iterator [Any ]:
373
402
"""Iterator over faces of a simplex or vertex sequence."""
374
403
if dim is None :
375
404
dim = self .dim
@@ -394,7 +423,7 @@ def containing(self, face):
394
423
"""Simplices containing a face."""
395
424
return set .intersection (* (self .vertex_to_simplices [i ] for i in face ))
396
425
397
- def _extend_hull (self , new_vertex , eps = 1e-8 ):
426
+ def _extend_hull (self , new_vertex : Any , eps : float = 1e-8 ) -> Any :
398
427
# count multiplicities in order to get all hull faces
399
428
multiplicities = Counter (face for face in self .faces ())
400
429
hull_faces = [face for face , count in multiplicities .items () if count == 1 ]
@@ -434,7 +463,13 @@ def _extend_hull(self, new_vertex, eps=1e-8):
434
463
435
464
return new_simplices
436
465
437
- def circumscribed_circle (self , simplex , transform ):
466
+ def circumscribed_circle (
467
+ self , simplex : Any , transform : ndarray
468
+ ) -> Union [
469
+ Tuple [Tuple [float64 , float64 , float64 , float64 ], float ],
470
+ Tuple [Tuple [float64 , float64 ], float ],
471
+ Tuple [Tuple [float64 , float64 , float64 ], float ],
472
+ ]:
438
473
"""Compute the center and radius of the circumscribed circle of a simplex.
439
474
440
475
Parameters
@@ -450,7 +485,9 @@ def circumscribed_circle(self, simplex, transform):
450
485
pts = np .dot (self .get_vertices (simplex ), transform )
451
486
return circumsphere (pts )
452
487
453
- def point_in_cicumcircle (self , pt_index , simplex , transform ):
488
+ def point_in_cicumcircle (
489
+ self , pt_index : int , simplex : Any , transform : ndarray
490
+ ) -> bool_ :
454
491
# return self.fast_point_in_circumcircle(pt_index, simplex, transform)
455
492
eps = 1e-8
456
493
@@ -460,10 +497,15 @@ def point_in_cicumcircle(self, pt_index, simplex, transform):
460
497
return np .linalg .norm (center - pt ) < (radius * (1 + eps ))
461
498
462
499
@property
463
- def default_transform (self ):
500
+ def default_transform (self ) -> ndarray :
464
501
return np .eye (self .dim )
465
502
466
- def bowyer_watson (self , pt_index , containing_simplex = None , transform = None ):
503
+ def bowyer_watson (
504
+ self ,
505
+ pt_index : int ,
506
+ containing_simplex : Optional [Any ] = None ,
507
+ transform : Optional [ndarray ] = None ,
508
+ ) -> Any :
467
509
"""Modified Bowyer-Watson point adding algorithm.
468
510
469
511
Create a hole in the triangulation around the new point,
@@ -523,10 +565,10 @@ def bowyer_watson(self, pt_index, containing_simplex=None, transform=None):
523
565
new_triangles = self .vertex_to_simplices [pt_index ]
524
566
return bad_triangles - new_triangles , new_triangles - bad_triangles
525
567
526
- def _simplex_is_almost_flat (self , simplex ) :
568
+ def _simplex_is_almost_flat (self , simplex : Any ) -> bool_ :
527
569
return self ._relative_volume (simplex ) < 1e-8
528
570
529
- def _relative_volume (self , simplex ) :
571
+ def _relative_volume (self , simplex : Any ) -> float64 :
530
572
"""Compute the volume of a simplex divided by the average (Manhattan)
531
573
distance of its vertices. The advantage of this is that the relative
532
574
volume is only dependent on the shape of the simplex and not on the
@@ -537,7 +579,12 @@ def _relative_volume(self, simplex):
537
579
average_edge_length = np .mean (np .abs (vectors ))
538
580
return self .volume (simplex ) / (average_edge_length ** self .dim )
539
581
540
- def add_point (self , point , simplex = None , transform = None ):
582
+ def add_point (
583
+ self ,
584
+ point : Any ,
585
+ simplex : Optional [Any ] = None ,
586
+ transform : Optional [ndarray ] = None ,
587
+ ) -> Any :
541
588
"""Add a new vertex and create simplices as appropriate.
542
589
543
590
Parameters
@@ -586,13 +633,13 @@ def add_point(self, point, simplex=None, transform=None):
586
633
self .vertices .append (point )
587
634
return self .bowyer_watson (pt_index , actual_simplex , transform )
588
635
589
- def volume (self , simplex ) :
636
+ def volume (self , simplex : Any ) -> float :
590
637
prefactor = np .math .factorial (self .dim )
591
638
vertices = np .array (self .get_vertices (simplex ))
592
639
vectors = vertices [1 :] - vertices [0 ]
593
640
return float (abs (fast_det (vectors )) / prefactor )
594
641
595
- def volumes (self ):
642
+ def volumes (self ) -> List [ float ] :
596
643
return [self .volume (sim ) for sim in self .simplices ]
597
644
598
645
def reference_invariant (self ):
@@ -609,21 +656,29 @@ def vertex_invariant(self, vertex):
609
656
"""Simplices originating from a vertex don't overlap."""
610
657
raise NotImplementedError
611
658
612
- def get_neighbors_from_vertices (self , simplex ) :
659
+ def get_neighbors_from_vertices (self , simplex : Any ) -> Any :
613
660
return set .union (* [self .vertex_to_simplices [p ] for p in simplex ])
614
661
615
- def get_face_sharing_neighbors (self , neighbors , simplex ) :
662
+ def get_face_sharing_neighbors (self , neighbors : Any , simplex : Any ) -> Any :
616
663
"""Keep only the simplices sharing a whole face with simplex."""
617
664
return {
618
665
simpl for simpl in neighbors if len (set (simpl ) & set (simplex )) == self .dim
619
666
} # they share a face
620
667
621
- def get_simplices_attached_to_points (self , indices ) :
668
+ def get_simplices_attached_to_points (self , indices : Any ) -> Any :
622
669
# Get all simplices that share at least a point with the simplex
623
670
neighbors = self .get_neighbors_from_vertices (indices )
624
671
return self .get_face_sharing_neighbors (neighbors , indices )
625
672
626
- def get_opposing_vertices (self , simplex ):
673
+ def get_opposing_vertices (
674
+ self ,
675
+ simplex : Union [
676
+ Tuple [int32 , int , int ],
677
+ Tuple [int32 , int32 , int ],
678
+ Tuple [int32 , int32 , int32 ],
679
+ Tuple [int , int , int ],
680
+ ],
681
+ ) -> Any :
627
682
if simplex not in self .simplices :
628
683
raise ValueError ("Provided simplex is not part of the triangulation" )
629
684
neighbors = self .get_simplices_attached_to_points (simplex )
@@ -641,7 +696,7 @@ def find_opposing_vertex(vertex):
641
696
return result
642
697
643
698
@property
644
- def hull (self ):
699
+ def hull (self ) -> Union [ Set [ int32 ], Set [ int ], Set [ Union [ int32 , int ]]] :
645
700
"""Compute hull from triangulation.
646
701
647
702
Parameters
0 commit comments