3
3
import networkx as nx
4
4
from matplotlib import pyplot as plt
5
5
import copy
6
+ import enum
7
+
8
+
9
+ class ORIENTATION (enum .Enum ):
10
+ CCW = 1
11
+ CW = - 1
6
12
7
13
8
14
def beta_clusters (
@@ -41,20 +47,56 @@ def dist(p1, p2):
41
47
return points
42
48
43
49
44
- class RandomPolygon (object ):
45
- def __init__ (self , n , points = None , holes = 2 ):
46
- if points is not None :
47
- self .points = points
48
- else :
49
- self .points = beta_clusters (clusters = 4 , ppc = n // 4 )
50
-
51
- self .nholes = holes
52
-
53
- self .G , self .dt , self .dt_orig = self .polygon (
54
- self .points ,
55
- holes = holes ,
56
- removals = n // 3 ,
57
- )
50
+ def reordercw (points : np .ndarray , tobe : str ):
51
+ """return numpy view of re-ordered points"""
52
+ cw = 0.0
53
+ for i in range (points .shape [0 ] - 1 ):
54
+ p1 , p2 = points [i ], points [i + 1 ]
55
+ cw += p2 [0 ] * p1 [1 ] - p1 [0 ] * p2 [1 ]
56
+ if cw >= 0 and tobe == ORIENTATION .CW :
57
+ return points
58
+ elif cw < 0 and tobe == ORIENTATION .CW :
59
+ print ("reversing" )
60
+ return points [::- 1 ]
61
+ elif cw <= 0 and tobe == ORIENTATION .CCW :
62
+ return points
63
+ elif cw > 0 and tobe == ORIENTATION .CCW :
64
+ print ("reversing" )
65
+ return points [::- 1 ]
66
+
67
+
68
+ class Polygon (object ):
69
+ def __init__ (self , boundaries : np .ndarray , holes : list ):
70
+ if boundaries .shape [0 ] < 3 :
71
+ raise ValueError ("Boundaries must have at least 3 points." )
72
+
73
+ # check orientation of inputs
74
+ boundaries = reordercw (boundaries , ORIENTATION .CW )
75
+ for i , hole in enumerate (holes ):
76
+ holes [i ] = reordercw (hole , ORIENTATION .CCW )
77
+
78
+ # create digraph from list
79
+ self .G = nx .DiGraph ()
80
+ node = 0
81
+ # add all points
82
+ for i in range (boundaries .shape [0 ] - 1 ):
83
+ self .G .add_node (node , points = boundaries [i ])
84
+ self .G .add_edge (node , node + 1 , weight = 1 )
85
+ node += 1
86
+ # add edge to close the loop and add last node
87
+ self .G .add_node (node , points = boundaries [- 1 ])
88
+ self .G .add_edge (node , 0 , weight = 1 )
89
+ node += 1
90
+ for hole in holes :
91
+ hole0 = node
92
+ for i in range (hole .shape [0 ] - 1 ):
93
+ self .G .add_node (node , points = hole [i ])
94
+ self .G .add_edge (node , node + 1 , weight = 2 )
95
+ node += 1
96
+ # add edge to close the loop and add last node
97
+ self .G .add_node (node , points = hole [- 1 ])
98
+ self .G .add_edge (node , hole0 , weight = 2 )
99
+ node += 1
58
100
59
101
def removable_interiors (self , dt : spatial .Delaunay ) -> tuple :
60
102
"""find indices of interior simplices that are safe to remove in dt"""
@@ -220,12 +262,12 @@ def polygon(
220
262
for (e1 , e2 ), c in zip (M .edges , cyc ):
221
263
outer = True
222
264
M [e1 ][e2 ]["weight" ] = 1
223
- cw += self .addcw (H , e1 , e2 )
265
+ cw += self .addcw_h (H , e1 , e2 )
224
266
else :
225
267
for (e1 , e2 ), c in zip (M .edges , cyc ):
226
268
M [e1 ][e2 ]["weight" ] = 2
227
269
outer = False
228
- cw += self .addcw (H , e1 , e2 )
270
+ cw += self .addcw_h (H , e1 , e2 )
229
271
cw = cw >= 0
230
272
# categorize nodes
231
273
# append
@@ -240,7 +282,7 @@ def polygon(
240
282
out_graph = nx .compose_all (outputgraphs )
241
283
return out_graph , dt , dt_orig
242
284
243
- def addcw (self , H : nx .DiGraph , e1 : int , e2 : int ) -> float :
285
+ def addcw_h (self , H : nx .DiGraph , e1 : int , e2 : int ) -> float :
244
286
"""determine which way the edge is pointing
245
287
246
288
e.g.
@@ -257,9 +299,11 @@ def addcw(self, H: nx.DiGraph, e1: int, e2: int) -> float:
257
299
p1 , p2 = H .nodes [e1 ]["points" ], H .nodes [e2 ]["points" ]
258
300
return (p2 [0 ] - p1 [0 ]) * (p2 [1 ] + p1 [1 ])
259
301
302
+ def cw (self , p1 , p2 ) -> float :
303
+ return (p2 [0 ] - p1 [0 ]) * (p2 [1 ] + p1 [1 ])
304
+
260
305
def plot (
261
306
self ,
262
- G : nx .DiGraph ,
263
307
ax : plt .Axes ,
264
308
posattr : str = "points" ,
265
309
arrows : bool = False ,
@@ -273,14 +317,14 @@ def plot(
273
317
draw_nodes = True ,
274
318
) -> plt .Axes :
275
319
"""Draw a DiGraph `G` with points stored in `posattr` onto `ax`"""
276
- pos = nx .get_node_attributes (G , posattr )
320
+ pos = nx .get_node_attributes (self . G , posattr )
277
321
if not ecolor :
278
322
try :
279
- ecolor = [G [u ][v ][ecolorattr ] for u , v in G .edges ()]
323
+ ecolor = [self . G [u ][v ][ecolorattr ] for u , v in self . G .edges ()]
280
324
except :
281
- ecolor = [5 for _ in G .edges ()]
325
+ ecolor = [5 for _ in self . G .edges ()]
282
326
nx .draw_networkx_edges (
283
- G ,
327
+ self . G ,
284
328
pos ,
285
329
ax = ax ,
286
330
node_size = 4 ,
@@ -291,20 +335,34 @@ def plot(
291
335
)
292
336
if draw_nodes :
293
337
nx .draw_networkx_nodes (
294
- G ,
338
+ self . G ,
295
339
pos ,
296
340
ax = ax ,
297
341
node_shape = m ,
298
342
node_color = nodecolor ,
299
343
node_size = 30 ,
300
344
)
301
345
if node_text :
302
- for n in G .nodes :
346
+ for n in self . G .nodes :
303
347
ax .text (pos [n ][0 ], pos [n ][1 ], s = str (n ))
304
348
ax .autoscale (tight = False )
305
349
return ax
306
350
307
351
352
+ class RandomPolygon (Polygon ):
353
+ def __init__ (self , n , points = None , holes = 2 ):
354
+ if points is not None :
355
+ self .points = points
356
+ else :
357
+ self .points = beta_clusters (clusters = 4 , ppc = n // 4 )
358
+ self .nholes = holes
359
+ self .G , self .dt , self .dt_orig = self .polygon (
360
+ self .points ,
361
+ holes = holes ,
362
+ removals = n // 3 ,
363
+ )
364
+
365
+
308
366
def stupid_spiky_polygon (
309
367
R_inner : int ,
310
368
R_outer : int ,
0 commit comments