ringspy 0.1.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,2021 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ RingsPy utilities and functions
4
+
5
+ author: king_yin3613
6
+ email: haoyin2022@u.northwestern.edu
7
+ """
8
+
9
+ import math
10
+ import bisect
11
+ import numpy as np
12
+ import scipy.stats
13
+ from scipy.spatial.distance import cdist
14
+ import matplotlib.pyplot as plt
15
+ from scipy.spatial import cKDTree
16
+ from pathlib import Path
17
+
18
+ def intersect2D(a, b):
19
+ """
20
+ Find row intersection between 2D numpy arrays, a and b.
21
+ Returns another numpy array with shared rows
22
+ credit to: https://gist.github.com/Robaina/b742f44f489a07cd26b49222f6063ef7
23
+ """
24
+ return np.array([x for x in set(tuple(x) for x in a) & set(tuple(x) for x in b)])
25
+
26
+
27
+ def TriangleArea2D(x1, y1, x2, y2, x3, y3):
28
+ """
29
+ A utility function to calculate area
30
+ of triangle formed by (x1, y1),
31
+ (x2, y2) and (x3, y3)
32
+ """
33
+ return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)
34
+
35
+ def check_point_boundbox(point,log_center,r):
36
+ ''' Make sure this point does not protrude outside the bounding circle '''
37
+ within = ( math.sqrt((point[0]-log_center[0])**2 + (point[1]-log_center[1])**2) <= r )
38
+
39
+ return within
40
+
41
+ def check_isinside_boundcircle(circC,log_center,rmin,rmax):
42
+ ''' Make sure this circle does not protrude outside the ring's bounding circle '''
43
+ x = circC[0]
44
+ y = circC[1]
45
+ w = circC[2]
46
+ within = ( math.sqrt((x-log_center[0])**2 + (y-log_center[1])**2) > rmin+w ) and ( math.sqrt((x-log_center[0])**2 + (y-log_center[1])**2)<rmax-w )
47
+
48
+ return within
49
+
50
+ def check_isinside_boundTriangle2D(circC,boundaries):
51
+ """
52
+ A function to check whether point P(x, y)
53
+ lies inside the triangle formed by
54
+ A(x1, y1), B(x2, y2) and C(x3, y3)
55
+ credit to: https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
56
+ """
57
+ x = circC[0]
58
+ y = circC[1]
59
+ w = circC[2]
60
+
61
+ x1 = boundaries[0][1][0][0]
62
+ y1 = boundaries[0][1][0][1]
63
+ x2 = boundaries[1][1][0][0]
64
+ y2 = boundaries[1][1][0][1]
65
+ x3 = boundaries[2][1][0][0]
66
+ y3 = boundaries[2][1][0][1]
67
+
68
+ # Calculate area of triangle ABC
69
+ A = TriangleArea2D(x1, y1, x2, y2, x3, y3)
70
+
71
+ # Calculate area of triangle PBC
72
+ A1 = TriangleArea2D(x, y, x2, y2, x3, y3)
73
+
74
+ # Calculate area of triangle PAC
75
+ A2 = TriangleArea2D(x1, y1, x, y, x3, y3)
76
+
77
+ # Calculate area of triangle PAB
78
+ A3 = TriangleArea2D(x1, y1, x2, y2, x, y)
79
+
80
+ # Check if sum of A1, A2 and A3 is same as A
81
+ # within = (A == A1 + A2 + A3)
82
+ # with tolerance
83
+ within = (np.abs(A - (A1 + A2 + A3)) <= A*1e-3)
84
+
85
+ return within
86
+
87
+ def check_isinside_boundbox2D(circC,x_min,x_max,y_min,y_max):
88
+ ''' Make sure this circle does not protrude outside the bounding box '''
89
+ x = circC[0]
90
+ y = circC[1]
91
+ w = circC[2]
92
+ within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
93
+
94
+ return within
95
+
96
+ def check_isinside_boundbox2Dindent(circC,x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
97
+ ''' Make sure this circle does not protrude outside the indent bounding box '''
98
+ x = circC[0]
99
+ y = circC[1]
100
+ w = circC[2]
101
+ within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
102
+ within_indent = ( x > x_min+w ) and ( x < x_indent-w ) and ( y > y_indent_min+w ) and ( y < y_indent_max-w )
103
+ within = np.subtract(within,within_indent,dtype=np.float32)
104
+ return within
105
+
106
+ def check_isinside_boundbox2Dprecrack(circC,x_min,x_max,y_min,y_max,x_indent,y_indent,x_precrack,y_precrack):
107
+ ''' Make sure this circle does not protrude outside the ring's bounding box '''
108
+ # Neither/nor set operation, ref:https://math.stackexchange.com/questions/1257097/how-to-represent-a-neither-nor-set-operation
109
+ x = circC[0]
110
+ y = circC[1]
111
+ w = circC[2]
112
+ within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
113
+ within_indent = ( x > x_min+w ) and ( x < x_indent-w ) and ( y > -y_indent+w ) and ( y < y_indent-w )
114
+ within_precrack = ( x > x_indent+w ) and ( x < x_precrack-w ) and ( y > -y_precrack+w ) and ( y < y_precrack-w )
115
+ within_both = within_indent or within_precrack
116
+ within = np.subtract(within,within_both,dtype=np.float32)
117
+ return within
118
+
119
+ def check_isinside_boundHexagon2D(circC,L,box_center):
120
+ ''' Make sure this circle does not protrude outside the bounding hexagon
121
+ credit to: http://www.playchilla.com/how-to-check-if-a-point-is-inside-a-hexagon
122
+ '''
123
+ x = np.abs(circC[0] - box_center[0])
124
+ y = np.abs(circC[1] - box_center[1])
125
+ v = L/2
126
+ h = L/np.sqrt(3)/2
127
+ within = ( x < 2*h ) and ( y < v ) and ((2*v*h - x*v - h*y) > 0)
128
+ return within
129
+
130
+ def check_overlap(circles,circC):
131
+ ''' Make sure the distance between the current circle's center and all
132
+ other circle centers is greater than or equal to the circle's perimeter (2r)
133
+ '''
134
+ x = circC[0]
135
+ y = circC[1]
136
+ w = circC[2]
137
+ nooverlap = all( (x - c[0])**2 + (y - c[1])**2 >= (w + c[2])**2 for c in circles )
138
+ return nooverlap
139
+
140
+ def check_iscollinear(p1,p2,boundaries):
141
+ ''' Make sure the line segment x1-x2 is collinear with
142
+ any line segment of the boundary polygon
143
+ '''
144
+ collinear = 0
145
+ if (p2[0]-p1[0]) == 0: # inf slope
146
+ k1 = 99999999
147
+ else:
148
+ k1 = (p2[1]-p1[1])/(p2[0]-p1[0]) # slope of the new line segment
149
+
150
+ for boundary in boundaries: # loop over boundary lines
151
+ bp1 = boundary[1][0]
152
+ bp2 = boundary[1][1]
153
+
154
+ if (bp2[0]-bp1[0]) == 0: # inf slope
155
+ k2 = 99999999
156
+ else:
157
+ k2 = (bp2[1]-bp1[1])/(bp2[0]-bp1[0]) # slope of the boundary line segment
158
+
159
+ if math.isclose(k1,k2): # check if slopes are equal (with a tolerance)
160
+ p3 = (p1+p2)/2 # mid point of the new line
161
+ if k2 == 99999999: # inf slope
162
+ p3_on = (p3[0] == bp1[0])
163
+ else:
164
+ p3_on = math.isclose((p3[1] - bp1[1]),k2*(p3[0] - bp1[0]))
165
+ p3_between = (min(bp1[0], bp2[0]) <= p3[0] <= max(bp1[0], bp2[0])) and (min(bp1[1], bp2[1]) <= p3[1] <= max(bp1[1], bp2[1]))
166
+ if (p3_on and p3_between): # check if mid point of the new line is on the boundary line segment
167
+ collinear += 1
168
+
169
+ return collinear
170
+
171
+ def rotate_around_point_highperf(xy, radians, origin=(0, 0)):
172
+ """Rotate a point around a given point.
173
+
174
+ I call this the "high performance" version since we're caching some
175
+ values that are needed >1 time. It's less readable than the previous
176
+ function but it's faster.
177
+ """
178
+ x, y = xy
179
+ offset_x, offset_y = origin
180
+ adjusted_x = (x - offset_x)
181
+ adjusted_y = (y - offset_y)
182
+ cos_rad = math.cos(radians)
183
+ sin_rad = math.sin(radians)
184
+ qx = offset_x + cos_rad * adjusted_x + sin_rad * adjusted_y
185
+ qy = offset_y + -sin_rad * adjusted_x + cos_rad * adjusted_y
186
+
187
+ return qx, qy
188
+
189
+
190
+ def CellPlacementWood(log_center,r_max,r_min,nrings,width_heart,width_early,width_late,\
191
+ cellsize_early,cellsize_late,iter_max,print_interval):
192
+ '''packing cells in annual rings'''
193
+
194
+ # Annual ring width distribution
195
+ width = np.concatenate(([width_heart],np.tile([width_early,width_late],nrings)))
196
+ noise = np.random.normal(1,0.25,len(width))
197
+ width = np.multiply(width,noise)
198
+ radius = np.concatenate(([0],np.cumsum(width)))
199
+
200
+ # Generate circle seeds in each annual ring (can be parallelized in the future)
201
+ circles = []
202
+ for ibin in range(0,nrings*2+1):
203
+ iter = 0
204
+ while iter < iter_max:
205
+ r = (radius[ibin+1]-radius[ibin])*math.sqrt(np.random.random()) + radius[ibin] # randomly generate a point in the domain
206
+ t = np.random.random()*2*math.pi
207
+
208
+ x = r*math.cos(t) + log_center[0]
209
+ y = r*math.sin(t) + log_center[1]
210
+
211
+ if (ibin % 2) == 0: # if even, latewood
212
+ w = cellsize_late #scipy.stats.truncnorm.rvs((we_lower - we_mu) / we_sigma, (we_upper - we_mu) / we_sigma, loc=we_mu, scale=we_sigma)
213
+ else:
214
+ w = cellsize_early #scipy.stats.truncnorm.rvs((wl_lower - wl_mu) / wl_sigma, (wl_upper - wl_mu) / wl_sigma, loc=wl_mu, scale=wl_sigma)
215
+
216
+ circC = [x, y, w]
217
+
218
+ if check_overlap(circles, circC) and check_isinside_boundcircle(circC,log_center,radius[ibin],radius[ibin+1]):
219
+ circles.append(circC)
220
+
221
+ # regularly print
222
+ if (len(circles) % print_interval == 0):
223
+ if (nrings*2+1-ibin == 1):
224
+ print('{:d} ring remaining; {:d} cells/particles placed.'.format(nrings*2+1-ibin,len(circles)))
225
+ else:
226
+ print('{:d} rings remaining; {:d} cells/particles placed.'.format(nrings*2+1-ibin,len(circles)))
227
+ iter += 1
228
+
229
+ circles = np.matrix(circles)
230
+
231
+ # take off out of bound Voronoi vertices
232
+ outofbound = []
233
+ for i in range(0,circles.shape[0]):
234
+ if( ((circles[i,0]-log_center[0])**2+(circles[i,1]-log_center[1])**2) > (1.2*r_max)**2 ):
235
+ outofbound.append(i)
236
+ circles = np.delete(circles,outofbound,0)
237
+
238
+ return circles, radius
239
+
240
+
241
+
242
+
243
+ def RebuildVoronoi(vor,circles,boundaries,log_center,x_min,x_max,y_min,y_max,box_center,boundaryFlag):
244
+ '''Clip Voronoi mesh by the boundaries, rebuild the new Voronoi mesh'''
245
+ # Store indices of Voronoi vertices for each finite ridge
246
+ finite_ridges = []
247
+ finite_ridges_pointid = []
248
+ infinite_ridges = []
249
+ infinite_ridges_pointid = []
250
+ boundary_points = []
251
+
252
+ # Form two new Voronoi vertices lists with and without out-of-bound vertices
253
+ voronoi_vertices_in = []
254
+ voronoi_vertices_out = []
255
+
256
+ if len(boundaries) == 3:
257
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
258
+ simplex = np.asarray(simplex)
259
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
260
+ if np.all(simplex >= 0) and check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) and check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries):
261
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
262
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
263
+ finite_ridges.append(simplex)
264
+ finite_ridges_pointid.append(pointidx)
265
+ plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
266
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
267
+ if np.all(simplex >= 0) and (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) or check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries)): # finite Voronoi ridge but one vertex is far
268
+
269
+ if (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) == 0):# if vertex is out-of-bound, inverse its index
270
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
271
+ if simplex[0] == 0:
272
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
273
+ else:
274
+ simplex[0] = -simplex[0]
275
+ elif (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries) == 0):
276
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
277
+ if simplex[1] == 0:
278
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
279
+ else:
280
+ simplex[1] = -simplex[1]
281
+ infinite_ridges.append(simplex)
282
+ infinite_ridges_pointid.append(pointidx)
283
+ elif check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),boundaries): # index of finite end Voronoi vertex of the infinite ridge
284
+ infinite_ridges.append(simplex)
285
+ infinite_ridges_pointid.append(pointidx)
286
+ elif len(boundaries) == 4:
287
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
288
+ simplex = np.asarray(simplex)
289
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
290
+ if np.all(simplex >= 0) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max):
291
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
292
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
293
+ finite_ridges.append(simplex)
294
+ finite_ridges_pointid.append(pointidx)
295
+ plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
296
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
297
+ if np.all(simplex >= 0) and (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) or check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max)): # finite Voronoi ridge but one vertex is far
298
+
299
+ if (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) == 0):# if vertex is out-of-bound, inverse its index
300
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
301
+ if simplex[0] == 0:
302
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
303
+ else:
304
+ simplex[0] = -simplex[0]
305
+ elif (check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max) == 0):
306
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
307
+ if simplex[1] == 0:
308
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
309
+ else:
310
+ simplex[1] = -simplex[1]
311
+ infinite_ridges.append(simplex)
312
+ infinite_ridges_pointid.append(pointidx)
313
+ elif check_isinside_boundbox2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max): # index of finite end Voronoi vertex of the infinite ridge
314
+ infinite_ridges.append(simplex)
315
+ infinite_ridges_pointid.append(pointidx)
316
+ elif len(boundaries) == 6:
317
+ L = y_max - y_min
318
+ box_center = log_center
319
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
320
+ simplex = np.asarray(simplex)
321
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
322
+ if np.all(simplex >= 0) and check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) and check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center):
323
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
324
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
325
+
326
+ finite_ridges.append(simplex)
327
+ finite_ridges_pointid.append(pointidx)
328
+ plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
329
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
330
+
331
+ if np.all(simplex >= 0) and (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) or check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center)): # finite Voronoi ridge but one vertex is far
332
+
333
+ if (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) == 0):# if vertex is out-of-bound, inverse its index
334
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
335
+ if simplex[0] == 0:
336
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
337
+ else:
338
+ simplex[0] = -simplex[0]
339
+ elif (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center) == 0):
340
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
341
+ if simplex[1] == 0:
342
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
343
+ else:
344
+ simplex[1] = -simplex[1]
345
+ infinite_ridges.append(simplex)
346
+ infinite_ridges_pointid.append(pointidx)
347
+ elif check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),L,box_center): # index of finite end Voronoi vertex of the infinite ridge
348
+ infinite_ridges.append(simplex)
349
+ infinite_ridges_pointid.append(pointidx)
350
+ elif len(boundaries) == 8: # grooved
351
+ x_min = boundaries[0][1][0][0]
352
+ x_max = boundaries[1][1][0][0]
353
+ y_min = boundaries[0][1][0][1]
354
+ y_max = boundaries[2][1][0][1]
355
+ x_indent = boundaries[5][1][0][0]
356
+ y_indent_min = boundaries[6][1][0][1]
357
+ y_indent_max = boundaries[4][1][0][1]
358
+
359
+
360
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
361
+ simplex = np.asarray(simplex)
362
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
363
+ if np.all(simplex >= 0) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
364
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
365
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
366
+ finite_ridges.append(simplex)
367
+ finite_ridges_pointid.append(pointidx)
368
+ plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-')
369
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
370
+ if np.all(simplex >= 0) and (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) or check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max)): # finite Voronoi ridge but one vertex is far
371
+
372
+ if (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):# if vertex is out-of-bound, inverse its index
373
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
374
+ if simplex[0] == 0:
375
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
376
+ else:
377
+ simplex[0] = -simplex[0]
378
+ elif (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):
379
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
380
+ if simplex[1] == 0:
381
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
382
+ else:
383
+ simplex[1] = -simplex[1]
384
+ infinite_ridges.append(simplex)
385
+ infinite_ridges_pointid.append(pointidx)
386
+ elif check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max): # index of finite end Voronoi vertex of the infinite ridge
387
+ infinite_ridges.append(simplex)
388
+ infinite_ridges_pointid.append(pointidx)
389
+ else:
390
+ return
391
+
392
+ voronoi_vertices_in = np.unique(voronoi_vertices_in,axis=0)
393
+ voronoi_vertices_out = np.unique(voronoi_vertices_out,axis=0)
394
+ # plt.plot(voronoi_vertices_in[:, 0], voronoi_vertices_in[:, 1], 'ro')
395
+
396
+
397
+
398
+ # Find the intersect point of infinite ridges (rays) with the boundary lines
399
+ # Ref: https://stackoverflow.com/questions/14307158/how-do-you-check-for-intersection-between-a-line-segment-and-a-line-ray-emanatin#:~:text=Let%20r%20%3D%20(cos%20%CE%B8%2C,0%20%E2%89%A4%20u%20%E2%89%A4%201).&text=Then%20your%20line%20segment%20intersects,0%20%E2%89%A4%20u%20%E2%89%A4%201.
400
+ for pointidx, simplex in zip(infinite_ridges_pointid, infinite_ridges):
401
+ simplex = np.asarray(simplex)
402
+
403
+ i = simplex[simplex >= 0][0] # index of finite end Voronoi vertex of the infinite ridge
404
+ p = vor.vertices[i]
405
+ s = circles[pointidx[1],0:2] - circles[pointidx[0],0:2]
406
+ if simplex[simplex < 0][0] == -9999999999:
407
+ tangent = s / np.linalg.norm(s)
408
+ normal = np.array([-tangent[1], tangent[0]]) # normal
409
+ midpoint = circles[:,0:2][pointidx].mean(axis=0) # midpoint between site points
410
+ for boundary in boundaries: # loop over boundary lines
411
+ q = np.asarray(boundary[1][0])
412
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
413
+ t = np.cross((q-p),s)/np.cross(normal,s)
414
+ u = np.cross((q-p),normal)/np.cross(normal,s)
415
+
416
+ if (u >= 0) and (u <= 1):
417
+ if np.sign(np.dot(midpoint - log_center, normal)) == 1: # facing outwards
418
+ if (t >= 0) and math.isfinite(t):
419
+ t_final = t
420
+ elif np.sign(np.dot(midpoint - log_center, normal)) == -1: # facing inwards
421
+ if (t < 0) and math.isfinite(t):
422
+ t_final = t
423
+
424
+ elif simplex[simplex < 0][0] == -9999999998:
425
+ t_final = np.inf
426
+ normal = (vor.vertices[0] - p)/np.linalg.norm(vor.vertices[0] - p)
427
+ for boundary in boundaries: # loop over boundary lines
428
+ q = np.asarray(boundary[1][0])
429
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
430
+ t = np.cross((q-p),s)/np.cross(normal,s)
431
+ u = np.cross((q-p),normal)/np.cross(normal,s)
432
+
433
+ if (u >= 0) and (u <= 1):
434
+ if (t >= 0) and (t < t_final):
435
+ t_final = t
436
+ else:
437
+ t_final = np.inf
438
+ normal = (vor.vertices[-simplex[simplex < 0][0]] - p)/np.linalg.norm(vor.vertices[-simplex[simplex < 0][0]] - p)
439
+ for boundary in boundaries: # loop over boundary lines
440
+ q = np.asarray(boundary[1][0])
441
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
442
+ t = np.cross((q-p),s)/np.cross(normal,s)
443
+ u = np.cross((q-p),normal)/np.cross(normal,s)
444
+
445
+ if (u >= 0) and (u <= 1):
446
+ if (t >= 0) and (t < t_final):
447
+ t_final = t
448
+
449
+ intersect_point = p + normal * t_final
450
+ boundary_points.append(intersect_point)
451
+
452
+ plt.plot([vor.vertices[i,0], intersect_point[0]], [vor.vertices[i,1], intersect_point[1]], 'k-',linewidth=1)
453
+
454
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
455
+ boundary_points = np.asarray(boundary_points)
456
+ boundary_points_featured = [x[1][0] for x in boundaries]
457
+ boundary_points_featured = np.reshape(np.asarray(boundary_points_featured),(-1,2))
458
+ nboundary_pts_featured = boundary_points_featured.shape[0]
459
+ boundary_points = np.vstack((boundary_points,boundary_points_featured))
460
+ nvertices_in = voronoi_vertices_in.shape[0]
461
+ nboundary_pts = boundary_points.shape[0]
462
+ finite_ridges = np.asarray(finite_ridges)
463
+ nfinite_ridge = finite_ridges.shape[0]
464
+ infinite_ridges = np.asarray(infinite_ridges)
465
+ ninfinite_ridge = infinite_ridges.shape[0]
466
+
467
+ else:
468
+ boundary_points = np.asarray(boundary_points)
469
+ nboundary_pts_featured = 0
470
+ nvertices_in = voronoi_vertices_in.shape[0]
471
+ nboundary_pts = boundary_points.shape[0]
472
+ finite_ridges = np.asarray(finite_ridges)
473
+ nfinite_ridge = finite_ridges.shape[0]
474
+ infinite_ridges = np.asarray(infinite_ridges)
475
+ ninfinite_ridge = infinite_ridges.shape[0]
476
+
477
+ # reconstruct the connectivity for ridges since the unique operation rearrange the order of vertices (need to find a more efficient way like vectorize)
478
+ finite_ridges_new = np.copy(finite_ridges)
479
+ for i in range(0,nfinite_ridge): # loop over voronoi ridges in original finite_ridge list
480
+ ii = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,0]],axis=1))[0][0] # index of first vertex in voronoi_vertices_in array
481
+ jj = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,1]],axis=1))[0][0] # index of second vertex in voronoi_vertices_in array
482
+ finite_ridges_new[i,:] = [ii,jj]
483
+
484
+ infinite_ridges_new = np.copy(infinite_ridges)
485
+ for i in range(0,ninfinite_ridge): # loop over voronoi ridges in original infinite_ridge list
486
+ if (infinite_ridges[i,:] < 0).argmax(axis=0) == 0: # first vertex is of negative index
487
+ ii = nvertices_in + i
488
+ jj = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,1]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
489
+ else:
490
+ ii = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,0]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
491
+ jj = nvertices_in + i
492
+ infinite_ridges_new[i,:] = [ii,jj]
493
+
494
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
495
+ # construct the connectivity for a line path consisting of the boundary points
496
+ boundary_ridges_new = np.zeros(boundary_points.shape)
497
+ boundary_points_new = np.copy(boundary_points)
498
+ next_point = np.copy(boundary_points_new[0]) # get first point
499
+ boundary_points_new[0] = [np.inf,np.inf]
500
+
501
+ for i in range(0,boundary_points_new.shape[0]-1):
502
+ next_point_id = cdist([next_point], boundary_points_new).argmin()
503
+ if check_iscollinear(next_point,boundary_points_new[next_point_id],boundaries) > 0: # check if the new line segment is collinear with any boundary line segment
504
+ boundary_ridges_new[i,1] = next_point_id
505
+ boundary_ridges_new[i+1,0] = next_point_id
506
+ next_point = np.copy(boundary_points_new[next_point_id])
507
+ boundary_points_new[next_point_id] = [np.inf,np.inf]
508
+ else:
509
+ boundary_points_new_check = np.copy(boundary_points_new)
510
+ while check_iscollinear(next_point,boundary_points_new_check[next_point_id],boundaries) == 0:
511
+ boundary_points_new_check[next_point_id] = [np.inf,np.inf]
512
+ next_point_id = cdist([next_point], boundary_points_new_check).argmin()
513
+
514
+ boundary_ridges_new[i,1] = next_point_id
515
+ boundary_ridges_new[i+1,0] = next_point_id
516
+ next_point = np.copy(boundary_points_new[next_point_id])
517
+ boundary_points_new[next_point_id] = [np.inf,np.inf]
518
+
519
+ boundary_ridges_new = (boundary_ridges_new+nvertices_in).astype(int) # shift the indices with "nvertices_in"
520
+
521
+ voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
522
+ nvertex = voronoi_vertices.shape[0]
523
+ boundary_ridges_new = np.vstack((infinite_ridges_new,boundary_ridges_new))
524
+ nboundary_ridge = boundary_ridges_new.shape[0]
525
+ voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
526
+ nridge = voronoi_ridges.shape[0]
527
+ else:
528
+ voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
529
+ nvertex = voronoi_vertices.shape[0]
530
+ boundary_ridges_new = np.copy(infinite_ridges_new)
531
+ nboundary_ridge = boundary_ridges_new.shape[0]
532
+ voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
533
+ nridge = voronoi_ridges.shape[0]
534
+
535
+ return voronoi_vertices,finite_ridges,boundary_points,finite_ridges_new,\
536
+ boundary_ridges_new,nvertex,nvertices_in,nfinite_ridge,nboundary_ridge,\
537
+ nboundary_pts,nboundary_pts_featured,voronoi_ridges,nridge
538
+
539
+ def RebuildVoronoi_new(vor,circles,boundaries,log_center,x_min,x_max,y_min,y_max,box_center,merge_tol,boundaryFlag):
540
+ '''Clip Voronoi mesh by the boundaries, rebuild the new Voronoi mesh'''
541
+ # Store indices of Voronoi vertices for each finite ridge
542
+ finite_ridges = []
543
+ finite_ridges_pointid = []
544
+ infinite_ridges = []
545
+ infinite_ridges_pointid = []
546
+ boundary_points = []
547
+
548
+ # Form two new Voronoi vertices lists with and without out-of-bound vertices
549
+ voronoi_vertices_in = []
550
+ voronoi_vertices_out = []
551
+
552
+ if len(boundaries) == 8: # grooved
553
+
554
+ x_min = boundaries[0][1][0][0]
555
+ x_max = boundaries[1][1][0][0]
556
+ y_min = boundaries[0][1][0][1]
557
+ y_max = boundaries[2][1][0][1]
558
+ x_indent = boundaries[5][1][0][0]
559
+ y_indent_min = boundaries[6][1][0][1]
560
+ y_indent_max = boundaries[4][1][0][1]
561
+
562
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
563
+ simplex = np.asarray(simplex)
564
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
565
+ if np.all(simplex >= 0) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
566
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
567
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
568
+ finite_ridges.append(simplex)
569
+ finite_ridges_pointid.append(pointidx)
570
+ plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-')
571
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
572
+ if np.all(simplex >= 0) and (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) or check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max)): # finite Voronoi ridge but one vertex is far
573
+ if (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):# if vertex is out-of-bound, inverse its index
574
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
575
+ if simplex[0] == 0:
576
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
577
+ else:
578
+ simplex[0] = -simplex[0]
579
+ elif (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):
580
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
581
+ if simplex[1] == 0:
582
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
583
+ else:
584
+ simplex[1] = -simplex[1]
585
+ infinite_ridges.append(simplex)
586
+ infinite_ridges_pointid.append(pointidx)
587
+ elif check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max): # index of finite end Voronoi vertex of the infinite ridge
588
+ infinite_ridges.append(simplex)
589
+ infinite_ridges_pointid.append(pointidx)
590
+ else:
591
+ for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
592
+ simplex = np.asarray(simplex)
593
+ simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
594
+ if np.all(simplex >= 0) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max):
595
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
596
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
597
+ finite_ridges.append(simplex)
598
+ finite_ridges_pointid.append(pointidx)
599
+ else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
600
+ if np.all(simplex >= 0) and (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) or check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max)): # finite Voronoi ridge but one vertex is far
601
+
602
+ if (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) == 0):# if vertex is out-of-bound, inverse its index
603
+ voronoi_vertices_out.append(vor.vertices[simplex[0]])
604
+ voronoi_vertices_in.append(vor.vertices[simplex[1]])
605
+ if simplex[0] == 0:
606
+ simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
607
+ else:
608
+ simplex[0] = -simplex[0]
609
+ elif (check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max) == 0):
610
+ voronoi_vertices_out.append(vor.vertices[simplex[1]])
611
+ voronoi_vertices_in.append(vor.vertices[simplex[0]])
612
+ if simplex[1] == 0:
613
+ simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
614
+ else:
615
+ simplex[1] = -simplex[1]
616
+ infinite_ridges.append(simplex)
617
+ infinite_ridges_pointid.append(pointidx)
618
+ elif check_isinside_boundbox2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max): # index of finite end Voronoi vertex of the infinite ridge
619
+ infinite_ridges.append(simplex)
620
+ infinite_ridges_pointid.append(pointidx)
621
+
622
+ voronoi_vertices_in = np.unique(voronoi_vertices_in,axis=0)
623
+ voronoi_vertices_out = np.unique(voronoi_vertices_out,axis=0)
624
+
625
+ # Find the intersect point of infinite ridges (rays) with the boundary lines
626
+ # Ref: https://stackoverflow.com/questions/14307158/how-do-you-check-for-intersection-between-a-line-segment-and-a-line-ray-emanatin#:~:text=Let%20r%20%3D%20(cos%20%CE%B8%2C,0%20%E2%89%A4%20u%20%E2%89%A4%201).&text=Then%20your%20line%20segment%20intersects,0%20%E2%89%A4%20u%20%E2%89%A4%201.
627
+ for pointidx, simplex in zip(infinite_ridges_pointid, infinite_ridges):
628
+ simplex = np.asarray(simplex)
629
+ i = simplex[simplex >= 0][0] # index of finite end Voronoi vertex of the infinite ridge
630
+ p = vor.vertices[i]
631
+ s = circles[pointidx[1],0:2] - circles[pointidx[0],0:2]
632
+ if simplex[simplex < 0][0] == -9999999999:
633
+ tangent = s / np.linalg.norm(s)
634
+ normal = np.array([-tangent[0,1], tangent[0,0]]) # normal
635
+ midpoint = circles[:,0:2][pointidx].mean(axis=0) # midpoint between site points
636
+ for boundary in boundaries: # loop over boundary lines
637
+ q = np.asarray(boundary[1][0])
638
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
639
+ t = np.cross((q-p),s)/np.cross(normal,s)
640
+ u = np.cross((q-p),normal)/np.cross(normal,s)
641
+
642
+ if (u>= 0) and (u<=1):
643
+ if np.sign(np.dot(midpoint - log_center, normal)) == 1: # facing outwards
644
+ if (t >= 0) and math.isfinite(t):
645
+ t_final = t
646
+ elif np.sign(np.dot(midpoint - log_center, normal)) == -1: # facing inwards
647
+ if (t < 0) and math.isfinite(t):
648
+ t_final = t
649
+
650
+ elif simplex[simplex < 0][0] == -9999999998:
651
+ t_final = np.inf
652
+ normal = (vor.vertices[0] - p)/np.linalg.norm(vor.vertices[0] - p)
653
+ for boundary in boundaries: # loop over boundary lines
654
+ q = np.asarray(boundary[1][0])
655
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
656
+ t = np.cross((q-p),s)/np.cross(normal,s)
657
+ u = np.cross((q-p),normal)/np.cross(normal,s)
658
+
659
+ if (u >= 0) and (u <= 1):
660
+ if (t > 0) and (t < t_final):
661
+ t_final = t
662
+ else:
663
+ t_final = np.inf
664
+ normal = (vor.vertices[-simplex[simplex < 0][0]] - p)/np.linalg.norm(vor.vertices[-simplex[simplex < 0][0]] - p)
665
+ for boundary in boundaries: # loop over boundary lines
666
+ q = np.asarray(boundary[1][0])
667
+ s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
668
+ t = np.cross((q-p),s)/np.cross(normal,s)
669
+ u = np.cross((q-p),normal)/np.cross(normal,s)
670
+
671
+ if (u >= 0) and (u <= 1):
672
+ if (t > 0) and (t < t_final):
673
+ t_final = t
674
+
675
+ intersect_point = p + normal * t_final
676
+ boundary_points.append(intersect_point)
677
+
678
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
679
+ boundary_points = np.asarray(boundary_points)
680
+ boundary_points_featured = [x[1][0] for x in boundaries]
681
+ boundary_points_featured = np.reshape(np.asarray(boundary_points_featured),(-1,2))
682
+ nboundary_pts_ini = boundary_points.shape[0]
683
+ nboundary_pts_featured = boundary_points_featured.shape[0]
684
+ boundary_points = np.vstack((boundary_points,boundary_points_featured))
685
+
686
+ nvertices_in = voronoi_vertices_in.shape[0]
687
+ nboundary_pts = boundary_points.shape[0]
688
+ finite_ridges = np.asarray(finite_ridges)
689
+ nfinite_ridge = finite_ridges.shape[0]
690
+ infinite_ridges = np.asarray(infinite_ridges)
691
+ ninfinite_ridge = infinite_ridges.shape[0]
692
+ else:
693
+ boundary_points = np.asarray(boundary_points)
694
+ nboundary_pts_featured = 0
695
+ nvertices_in = voronoi_vertices_in.shape[0]
696
+ nboundary_pts = boundary_points.shape[0]
697
+ finite_ridges = np.asarray(finite_ridges)
698
+ nfinite_ridge = finite_ridges.shape[0]
699
+ infinite_ridges = np.asarray(infinite_ridges)
700
+ ninfinite_ridge = infinite_ridges.shape[0]
701
+
702
+ # reconstruct the connectivity for ridges since the unique operation rearrange the order of vertices (need to find a more efficient way like vectorize)
703
+ finite_ridges_new = np.copy(finite_ridges)
704
+ for i in range(0,nfinite_ridge): # loop over voronoi ridges in original finite_ridge list
705
+ ii = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,0]],axis=1))[0][0] # index of first vertex in voronoi_vertices_in array
706
+ jj = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,1]],axis=1))[0][0] # index of second vertex in voronoi_vertices_in array
707
+ finite_ridges_new[i,:] = [ii,jj]
708
+
709
+ infinite_ridges_new = np.copy(infinite_ridges)
710
+ for i in range(0,ninfinite_ridge): # loop over voronoi ridges in original infinite_ridge list
711
+ if (infinite_ridges[i,:] < 0).argmax(axis=0) == 0: # first vertex is of negative index
712
+ ii = nvertices_in + i
713
+ jj = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,1]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
714
+ else:
715
+ ii = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,0]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
716
+ jj = nvertices_in + i
717
+ infinite_ridges_new[i,:] = [ii,jj]
718
+
719
+
720
+ # Merge operation
721
+ # Case 1 merge
722
+ voronoi_vertices_in_whole = np.copy(voronoi_vertices_in)
723
+ tree_voronoi_vertices_in = cKDTree(voronoi_vertices_in)
724
+ rows_to_fuse_voronoi_vertices_in = tree_voronoi_vertices_in.query_pairs(r=merge_tol,output_type='ndarray')
725
+ rows_to_fuse_voronoi_vertices_in_whole = np.copy(rows_to_fuse_voronoi_vertices_in)
726
+
727
+ finite_ridges_merged = np.copy(finite_ridges_new)
728
+ infinite_ridges_merged = np.copy(infinite_ridges_new)
729
+ while len(rows_to_fuse_voronoi_vertices_in) != 0:
730
+ points_new = np.reshape(voronoi_vertices_in_whole[rows_to_fuse_voronoi_vertices_in_whole],(-1,2))
731
+ voronoi_vertices_in_fused = 0.5*(points_new[0::2] + points_new[1::2])
732
+
733
+ # voronoi_vertices_in_fused = voronoi_vertices_in_fused.round(decimals=0)
734
+ voronoi_vertices_in_fused = np.round(voronoi_vertices_in_fused/merge_tol).astype(int)*merge_tol # here we have to round to the nearest merge_tol number to avoid infinite loop
735
+
736
+ # check if there are identical merged points from different pairs of closeby points (usually happens when three points are clustered)
737
+ if len(np.unique(voronoi_vertices_in_fused,axis=0)) != len(voronoi_vertices_in_fused):
738
+ [unique_pts,unique_inverse] = np.unique(voronoi_vertices_in_fused,return_inverse=True,axis=0)
739
+
740
+ # Offset the indices of boundary points as we inserted points in vertice_in list
741
+ infinite_ridges_merged[infinite_ridges_merged >= nvertices_in] += len(unique_pts)
742
+ # Replace the indices of two close points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
743
+ for i in range(0,len(rows_to_fuse_voronoi_vertices_in_whole)):
744
+ finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + unique_inverse[i]
745
+ finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + unique_inverse[i]
746
+ infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + unique_inverse[i]
747
+ infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + unique_inverse[i]
748
+ voronoi_vertices_in_fused = unique_pts.astype(float)
749
+ else:
750
+ # Offset the indices of boundary points as we inserted points in vertice_in list
751
+ infinite_ridges_merged[infinite_ridges_merged >= nvertices_in] += len(rows_to_fuse_voronoi_vertices_in_whole)
752
+ # Replace the indices of two close points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
753
+ for i in range(0,len(rows_to_fuse_voronoi_vertices_in_whole)):
754
+ finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + i
755
+ finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + i
756
+ infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + i
757
+ infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + i
758
+
759
+ voronoi_vertices_in_whole = np.vstack((voronoi_vertices_in_whole,voronoi_vertices_in_fused)) # create a whole vertex list
760
+ voronoi_vertices_in_new = np.vstack((np.delete(voronoi_vertices_in,rows_to_fuse_voronoi_vertices_in,0),voronoi_vertices_in_fused)) # create a new vertex list
761
+ # update the voronoi_vertices_in
762
+ voronoi_vertices_in = np.copy(voronoi_vertices_in_new)
763
+ nvertices_in = voronoi_vertices_in_whole.shape[0]
764
+ # update the kd-tree
765
+ tree_voronoi_vertices_in = cKDTree(voronoi_vertices_in)
766
+ rows_to_fuse_voronoi_vertices_in = tree_voronoi_vertices_in.query_pairs(r=merge_tol,output_type='ndarray')
767
+ # update the rows_to_fuse_voronoi_vertices_in with the rows-to-fuse in list voronoi_vertices_in_whole
768
+ rows_to_fuse_voronoi_vertices_in_whole = np.copy(rows_to_fuse_voronoi_vertices_in)
769
+ for i in range(0,len(rows_to_fuse_voronoi_vertices_in)):
770
+ rows_to_fuse_voronoi_vertices_in_whole[i,0] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in_new[rows_to_fuse_voronoi_vertices_in[i,0],:],axis=1))[0][0]
771
+ rows_to_fuse_voronoi_vertices_in_whole[i,1] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in_new[rows_to_fuse_voronoi_vertices_in[i,1],:],axis=1))[0][0]
772
+
773
+ # delete rows with identical indices and reverse indices in finite_ridges_merged
774
+ zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
775
+ finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
776
+
777
+ # delete rows with identical indices and reverse indices in infinite_ridges_merged
778
+ zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
779
+ infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
780
+
781
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
782
+ # construct the connectivity for a line path consisting of the boundary points
783
+ ####################################################################################################################################################
784
+ # The algorithm here needs improve: bug contains when searching for the closest point, the closest point may be on the other sides
785
+ ####################################################################################################################################################
786
+ boundary_points_ini = np.copy(boundary_points)
787
+ boundary_ridges_new = np.zeros(boundary_points.shape)
788
+ boundary_points_new = np.copy(boundary_points)
789
+ next_point = np.copy(boundary_points_new[0]) # get first point
790
+ boundary_points_new[0] = [np.inf,np.inf]
791
+
792
+ for i in range(0,boundary_points_new.shape[0]-1):
793
+ next_point_id = cdist([next_point], boundary_points_new).argmin()
794
+ boundary_ridges_new[i,1] = next_point_id
795
+ boundary_ridges_new[i+1,0] = next_point_id
796
+ next_point = np.copy(boundary_points_new[next_point_id])
797
+ boundary_points_new[next_point_id] = [np.inf,np.inf]
798
+ boundary_ridges_new = (boundary_ridges_new+nvertices_in).astype(int) # shift the indices with "nvertices_in"
799
+
800
+ # Case 2 merge
801
+ tree_boundary_points = cKDTree(boundary_points)
802
+ rows_to_fuse_boundary_points = tree_boundary_points.query_pairs(r=merge_tol,output_type='ndarray')
803
+ rows_to_fuse_boundary_points_whole = np.copy(rows_to_fuse_boundary_points)
804
+ boundary_ridges_merged = np.copy(boundary_ridges_new)
805
+ nboundary_pts = boundary_points.shape[0]
806
+ boundary_points_whole = np.copy(boundary_points)
807
+
808
+ while len(rows_to_fuse_boundary_points) != 0:
809
+ points_new = np.reshape(boundary_points_whole[rows_to_fuse_boundary_points_whole],(-1,2))
810
+ boundary_points_fused = 0.5*(points_new[0::2] + points_new[1::2])
811
+
812
+ # boundary_points_fused = boundary_points_fused.round(decimals=0)
813
+ boundary_points_fused = np.round(boundary_points_fused/merge_tol).astype(int)*merge_tol # here we have to round to the nearest merge_tol number to avoid infinite loop
814
+
815
+ # check if there are identical merged points from different pairs of closeby points (usually happens when three points are clustered)
816
+ if len(np.unique(boundary_points_fused,axis=0)) != len(boundary_points_fused):
817
+ [unique_pts,unique_inverse] = np.unique(boundary_points_fused,return_inverse=True,axis=0)
818
+ # Replace the indices of closeby points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
819
+ for i in range(0,len(rows_to_fuse_boundary_points)):
820
+ boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
821
+ boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
822
+ infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
823
+ infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
824
+ boundary_points_fused = unique_pts.astype(float)
825
+ else:
826
+ # Replace the indices of two close points with the index of new merged point in both boundary_ridges_new and infinite_ridges_new lists
827
+ for i in range(0,len(rows_to_fuse_boundary_points)):
828
+ boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + i
829
+ boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + i
830
+ infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + i
831
+ infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + i
832
+
833
+ boundary_points_whole = np.vstack((boundary_points_whole,boundary_points_fused)) # create a whole vertex list
834
+ boundary_points_new = np.vstack((np.delete(boundary_points,rows_to_fuse_boundary_points,0),boundary_points_fused)) # create a new vertex list
835
+
836
+
837
+ # update the boundary_points
838
+ boundary_points = np.copy(boundary_points_new)
839
+ nboundary_pts = boundary_points_whole.shape[0]
840
+ # update the kd-tree
841
+ tree_boundary_points = cKDTree(boundary_points)
842
+ rows_to_fuse_boundary_points = tree_boundary_points.query_pairs(r=merge_tol,output_type='ndarray')
843
+ # update the rows_to_fuse in list voronoi_vertices_in with the rows-to-fuse in list voronoi_vertices_in_whole
844
+ rows_to_fuse_boundary_points_whole = np.copy(rows_to_fuse_boundary_points)
845
+ for i in range(0,len(rows_to_fuse_boundary_points)):
846
+ rows_to_fuse_boundary_points_whole[i,0] = np.where(np.all(boundary_points_whole==boundary_points_new[rows_to_fuse_boundary_points[i,0],:],axis=1))[0][0]
847
+ rows_to_fuse_boundary_points_whole[i,1] = np.where(np.all(boundary_points_whole==boundary_points_new[rows_to_fuse_boundary_points[i,1],:],axis=1))[0][0]
848
+
849
+
850
+ # delete rows with identical indices and reverse indices in finite_ridges_merged
851
+ zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
852
+ boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
853
+
854
+ # delete rows with identical indices and reverse indices in infinite_ridges_merged
855
+ zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
856
+ infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
857
+
858
+ # replace too close boundary points with the featured
859
+ tree_boundary_points_whole = cKDTree(boundary_points_whole)
860
+ for i in range(0,boundary_points_featured.shape[0]):
861
+ rows_to_replace_boundary_points_whole = tree_boundary_points_whole.query_ball_point(boundary_points_featured[i,:],merge_tol)
862
+ if len(rows_to_replace_boundary_points_whole) != 0:
863
+ # Visualize nearby points
864
+ nearby_points = boundary_points_whole[rows_to_replace_boundary_points_whole]
865
+ # replace indices
866
+ for j in range(0,len(rows_to_replace_boundary_points_whole)):
867
+ boundary_ridges_merged[boundary_ridges_merged == (rows_to_replace_boundary_points_whole[j]+nvertices_in)] = nvertices_in + nboundary_pts_ini + i
868
+ infinite_ridges_merged[infinite_ridges_merged == (rows_to_replace_boundary_points_whole[j]+nvertices_in)] = nvertices_in + nboundary_pts_ini + i
869
+
870
+ # delete rows with identical indices and reverse indices in boundary_ridges_merged
871
+ zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
872
+ boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
873
+
874
+ # delete rows with identical indices and reverse indices in infinite_ridges_merged
875
+ zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
876
+ infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
877
+
878
+ # Case 3 merge (after loop of case 1 and 2)
879
+ for i in range(0,len(boundary_points)):
880
+ rows_to_replace_voronoi_vertices_in = tree_voronoi_vertices_in.query_ball_point(boundary_points[i,:],merge_tol)
881
+ if len(rows_to_replace_voronoi_vertices_in) != 0:
882
+ # update the rows_to_place_voronoi_vertices_in with the rows-to-replace in list voronoi_vertices_in_whole
883
+ rows_to_replace_voronoi_vertices_in_whole = np.copy(rows_to_replace_voronoi_vertices_in)
884
+ for j in range(0,len(rows_to_replace_voronoi_vertices_in_whole)):
885
+ rows_to_replace_voronoi_vertices_in_whole[j] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in[rows_to_replace_voronoi_vertices_in[j],:],axis=1))[0][0]
886
+
887
+ # Visualize nearby points
888
+ nearby_points = voronoi_vertices_in_whole[rows_to_replace_voronoi_vertices_in_whole]
889
+ # replace indices
890
+ for rows in rows_to_replace_voronoi_vertices_in_whole:
891
+ finite_ridges_merged[finite_ridges_merged == rows] = np.where(np.all(boundary_points_whole == boundary_points[i,:],axis=1))[0][0] + nvertices_in
892
+ infinite_ridges_merged[infinite_ridges_merged == rows] = np.where(np.all(boundary_points_whole == boundary_points[i,:],axis=1))[0][0] + nvertices_in
893
+
894
+ zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
895
+ finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
896
+
897
+ # delete rows with identical indices and reverse indices in infinite_ridges_merged
898
+ zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
899
+ infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
900
+
901
+ voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
902
+ voronoi_vertices_whole = np.vstack((voronoi_vertices_in_whole,boundary_points_whole)) # vertical stack "in" vetices and "cross" boundary points
903
+
904
+ all_ridges_merged = np.vstack((finite_ridges_merged,infinite_ridges_merged,boundary_ridges_merged))
905
+
906
+ else:
907
+ boundary_points_whole = np.copy(boundary_points)
908
+ voronoi_vertices_whole = np.vstack((voronoi_vertices_in_whole,boundary_points_whole)) # vertical stack "in" vetices and "cross" boundary points
909
+ all_ridges_merged = np.vstack((finite_ridges_merged,infinite_ridges_merged))
910
+
911
+ # Visualize merged mesh
912
+ for vpair in all_ridges_merged:
913
+ plt.plot([voronoi_vertices_whole[vpair[0],0], voronoi_vertices_whole[vpair[1],0]],[voronoi_vertices_whole[vpair[0],1], voronoi_vertices_whole[vpair[1],1]], 'g--')
914
+
915
+
916
+ voronoi_vertices = np.unique(np.reshape(voronoi_vertices_whole[all_ridges_merged],(-1,2)),axis=0)
917
+ # update the indices from voronoi_vertices_whole to voronoi_vertices(which is the list with no useless points)
918
+ for i in range(0,len(finite_ridges_merged)):
919
+ finite_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[finite_ridges_merged[i,0],:],axis=1))[0][0]
920
+ finite_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[finite_ridges_merged[i,1],:],axis=1))[0][0]
921
+ for i in range(0,len(infinite_ridges_merged)):
922
+ infinite_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[infinite_ridges_merged[i,0],:],axis=1))[0][0]
923
+ infinite_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[infinite_ridges_merged[i,1],:],axis=1))[0][0]
924
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
925
+ for i in range(0,len(boundary_ridges_merged)):
926
+ boundary_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[boundary_ridges_merged[i,0],:],axis=1))[0][0]
927
+ boundary_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[boundary_ridges_merged[i,1],:],axis=1))[0][0]
928
+ # delete rows with identical indices and reverse indices in finite_ridges_merged
929
+ zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
930
+ finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
931
+ # delete rows with identical indices and reverse indices in infinite_ridges_merged
932
+ zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
933
+ infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
934
+
935
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
936
+ # delete rows with identical indices and reverse indices in boundary_ridges_merged
937
+ zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
938
+ boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
939
+
940
+ # voronoi_vertices = voronoi_vertices_whole
941
+ nvertex = voronoi_vertices.shape[0]
942
+ finite_ridges_new = finite_ridges_merged
943
+ nfinite_ridge = finite_ridges_new.shape[0]
944
+ if boundaryFlag in ['on','On','Y','y','Yes','yes']:
945
+ boundary_ridges_new = np.vstack((infinite_ridges_merged,boundary_ridges_merged))
946
+ nboundary_ridge = boundary_ridges_new.shape[0]
947
+ voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
948
+ nridge = voronoi_ridges.shape[0]
949
+
950
+ intersect_temp = intersect2D(np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0),np.unique(np.reshape(voronoi_vertices_whole[boundary_ridges_merged],(-1,2)),axis=0))
951
+ nvertices_in = np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0).shape[0] - len(intersect_temp)
952
+ nboundary_pts = np.unique(np.reshape(voronoi_vertices_whole[boundary_ridges_merged],(-1,2)),axis=0).shape[0]
953
+ else:
954
+ boundary_ridges_new = np.copy(infinite_ridges_merged)
955
+ nboundary_ridge = boundary_ridges_new.shape[0]
956
+ voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
957
+ nridge = voronoi_ridges.shape[0]
958
+
959
+ nvertices_in = np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0).shape[0]
960
+
961
+ return voronoi_vertices,finite_ridges,boundary_points,finite_ridges_new,\
962
+ boundary_ridges_new,nvertex,nvertices_in,nfinite_ridge,nboundary_ridge,\
963
+ nboundary_pts,nboundary_pts_featured,voronoi_ridges,nridge
964
+
965
+
966
+ def RidgeMidQuarterPts(voronoi_vertices,nvertex,nvertices_in,voronoi_ridges,\
967
+ finite_ridges_new,boundary_ridges_new,nfinite_ridge,\
968
+ nboundary_ridge,nboundary_pts,nboundary_pts_featured):
969
+ ######################### For finite Voronoi ridges ###########################
970
+ # Form a list of middle points of finite Voronoi edges
971
+ finite_ridge_mid = []
972
+ finite_midpt_indices = []
973
+ count = nvertex
974
+
975
+ for vpair in finite_ridges_new:
976
+ midpoint = (voronoi_vertices[vpair[0]] + voronoi_vertices[vpair[1]])/2
977
+ finite_ridge_mid.append(midpoint)
978
+ finite_midpt_indices.append(count)
979
+ count += 1
980
+
981
+ finite_ridge_mid = np.tile(finite_ridge_mid, (2,1)) # duplicate the mid point list
982
+ finite_second_midpt_indices = [x+nfinite_ridge for x in finite_midpt_indices]
983
+ finite_midpt_indices = np.concatenate((finite_midpt_indices,finite_second_midpt_indices))
984
+ count += nfinite_ridge
985
+ nfinite_midpt = nfinite_ridge*2
986
+
987
+ # Form a list of quarter points of Voronoi edges
988
+ finite_ridge_quarter = []
989
+ finite_quarterpt_indices = []
990
+ for vpair in finite_ridges_new:
991
+ quarterpoint = 3./4*voronoi_vertices[vpair[0]] + 1./4*voronoi_vertices[vpair[1]]
992
+ finite_ridge_quarter.append(quarterpoint)
993
+ finite_quarterpt_indices.append(count)
994
+ count += 1
995
+
996
+ for vpair in finite_ridges_new:
997
+ quarterpoint = 1./4*voronoi_vertices[vpair[0]] + 3./4*voronoi_vertices[vpair[1]]
998
+ finite_ridge_quarter.append(quarterpoint)
999
+ finite_quarterpt_indices.append(count)
1000
+ count += 1
1001
+ finite_quarterpt_indices = np.asarray(finite_quarterpt_indices)
1002
+
1003
+ nfinite_quarterpt = nfinite_ridge*2
1004
+
1005
+ nfinitept_per_layer = nvertices_in + nfinite_midpt + nfinite_quarterpt
1006
+
1007
+ ######################### For boundary Voronoi ridges #########################
1008
+ # Form a list of mid-points of boundary Voronoi edges
1009
+ boundary_ridge_mid = []
1010
+ boundary_midpt_indices = []
1011
+ for vpair in boundary_ridges_new:
1012
+ boundary_midpoint = (voronoi_vertices[vpair[0]] + voronoi_vertices[vpair[1]])/2
1013
+ boundary_ridge_mid.append(boundary_midpoint)
1014
+ boundary_midpt_indices.append(count)
1015
+ count += 1
1016
+
1017
+ boundary_ridge_mid = np.tile(boundary_ridge_mid, (2,1))
1018
+ boundary_second_midpt_indices = [x+nboundary_ridge for x in boundary_midpt_indices]
1019
+ boundary_midpt_indices = np.concatenate((boundary_midpt_indices,boundary_second_midpt_indices))
1020
+ count += nboundary_ridge
1021
+ nboundary_midpt = nboundary_ridge*2
1022
+
1023
+ # Form a list of quarter-points of boundary Voronoi edges
1024
+ boundary_ridge_quarter = []
1025
+ boundary_quarterpt_indices = []
1026
+ for vpair in boundary_ridges_new:
1027
+ boundary_quarterpoint = 3./4*voronoi_vertices[vpair[0]] + 1./4*voronoi_vertices[vpair[1]]
1028
+ boundary_ridge_quarter.append(boundary_quarterpoint)
1029
+ boundary_quarterpt_indices.append(count)
1030
+ count += 1
1031
+
1032
+ for vpair in boundary_ridges_new:
1033
+ boundary_quarterpoint = 1./4*voronoi_vertices[vpair[0]] + 3./4*voronoi_vertices[vpair[1]]
1034
+ boundary_ridge_quarter.append(boundary_quarterpoint)
1035
+ boundary_quarterpt_indices.append(count)
1036
+ count += 1
1037
+ boundary_quarterpt_indices = np.asarray(boundary_quarterpt_indices)
1038
+
1039
+
1040
+ nboundary_quarterpt = nboundary_ridge*2
1041
+
1042
+ nboundary_pt_per_layer = nboundary_pts + nboundary_midpt + nboundary_quarterpt
1043
+
1044
+
1045
+ npt_per_layer = nvertices_in + nboundary_pts
1046
+ npt_per_layer_normal = npt_per_layer - nboundary_pts_featured
1047
+ npt_per_layer_vtk = nfinitept_per_layer + nboundary_pt_per_layer
1048
+
1049
+ all_midpt_indices = np.vstack((np.reshape(finite_midpt_indices,(2,-1)).T,np.reshape(boundary_midpt_indices,(2,-1)).T))
1050
+ all_quarterpt_indices = np.vstack((np.reshape(finite_quarterpt_indices,(2,-1)).T,np.reshape(boundary_quarterpt_indices,(2,-1)).T))
1051
+
1052
+ all_pts_2D = np.vstack((voronoi_vertices,finite_ridge_mid,finite_ridge_quarter,boundary_ridge_mid,boundary_ridge_quarter))
1053
+ all_ridges = np.hstack((voronoi_ridges,all_midpt_indices,all_quarterpt_indices))
1054
+
1055
+ return all_pts_2D,all_ridges,npt_per_layer,npt_per_layer_normal,npt_per_layer_vtk
1056
+
1057
+
1058
+
1059
+ def VertexandRidgeinfo(all_pts_2D,all_ridges,npt_per_layer,npt_per_layer_normal,\
1060
+ npt_per_layer_vtk,nridge,geoName,radius,log_center,\
1061
+ cellwallthickness_early,cellwallthickness_late):
1062
+
1063
+ '''Generate the vertex and ridge info
1064
+ Vertex info includes: coordinates, ridge indices, indices of another vertex for each ridge, ridge lengths, ridge angles, ridge width
1065
+ Ridge info includes: 2 vertex indices, 2 mid point indices, 2 quarter point indices '''
1066
+
1067
+
1068
+ # Calculate lengths of all Voronoi ridges
1069
+ vector = all_pts_2D[all_ridges[:,1],:] - all_pts_2D[all_ridges[:,0],:]
1070
+ all_ridge_lengths = np.linalg.norm(vector, axis=1)
1071
+
1072
+ # Calculate angles of all Voronoi ridges (angles measured counter-clock wise, x-axis --> (1,0), y-axis --> (0,1))
1073
+ all_ridge_angles = np.arctan2(vector[:,1],vector[:,0]) # np.arctan2(y, x) * 180 / np.pi = the angle
1074
+
1075
+ #======== This part can be expanded to allow the smooth transition of cell wall thickness =========
1076
+ # Case 1: Abrupt transition of cell wall thickness between earlywood/latewood
1077
+ # thicknesses of all Voronoi vertices (here identical thickness for all wings belonging to the same vertex is assumed)
1078
+ vertex_cellwallthickness_2D = np.zeros(npt_per_layer)
1079
+
1080
+ vertex_distance2logcenter = np.linalg.norm(all_pts_2D - log_center, axis=1) # calc distance between vertex and logcenter
1081
+ for i in range(0,npt_per_layer):
1082
+ if (bisect.bisect(radius,vertex_distance2logcenter[i]) % 2) != 0: # if odd, earlywood
1083
+ vertex_cellwallthickness_2D[i] = cellwallthickness_early
1084
+ else: # if even, latewood
1085
+ vertex_cellwallthickness_2D[i] = cellwallthickness_late
1086
+ #==================================================================================================
1087
+
1088
+ # Generate a list containing info for each vertex
1089
+ all_vertices_info_2D = []
1090
+ all_direction_info_2D = []
1091
+ for i in range(0,npt_per_layer): # loop over all vertices
1092
+
1093
+ allrows = np.where(all_ridges == i)[0] # find all Voronoi ridges containing the ith vertex
1094
+ vertex_info = []
1095
+ vertex_info.append(len(allrows)) # append total number of wings for the ith vertex
1096
+ vertex_info.append(allrows) # append ridge indices
1097
+ vertex_info.append(all_ridges[allrows,0:2][np.nonzero(all_ridges[allrows,0:2]-i)]) # append the indices of vetices at the other end of each ridge
1098
+ direction_info = []
1099
+ direction = np.where(all_ridges[allrows,0:2] == i)[1] # the position of current vertex in each ridge, p1 --> 0, p2 --> 1
1100
+ direction = -np.power(-1,direction+1) # convert the way to express the direction of each ridge to: 1/-1 --> pointing from/to the current vertex
1101
+ direction_info.append(direction) # append ridge direction
1102
+ vertex_info.append(all_ridge_lengths[allrows]/2) # append wing lengths
1103
+ vertex_info.append(np.ones(len(allrows))*vertex_cellwallthickness_2D[i]) # append wing widths
1104
+ vertex_info.append(all_ridge_angles[allrows]+math.pi*(-direction.clip(max=0))) # append wing angles
1105
+
1106
+ all_vertices_info_2D.append(vertex_info)
1107
+ all_direction_info_2D.append(direction_info)
1108
+
1109
+ # flattened 1D numpy array of lengths, widths and angles of wings
1110
+ # ref:https://stackoverflow.com/questions/952914/how-do-i-make-a-flat-list-out-of-a-list-of-lists
1111
+ ridge_inices = [vertex_info[1] for vertex_info in all_vertices_info_2D]
1112
+ ridge_inices = np.array([item for sublist in ridge_inices for item in sublist])
1113
+ another_vertex_inices = [vertex_info[2] for vertex_info in all_vertices_info_2D]
1114
+ another_vertex_inices = np.array([item for sublist in another_vertex_inices for item in sublist])
1115
+ ridge_lengths = [vertex_info[3] for vertex_info in all_vertices_info_2D]
1116
+ ridge_lengths = np.array([item for sublist in ridge_lengths for item in sublist])
1117
+ ridge_widths = [vertex_info[4] for vertex_info in all_vertices_info_2D]
1118
+ ridge_widths = np.array([item for sublist in ridge_widths for item in sublist])
1119
+ ridge_angles = [vertex_info[5] for vertex_info in all_vertices_info_2D]
1120
+ ridge_angles = np.array([item for sublist in ridge_angles for item in sublist])
1121
+ ridge_directions = [direction_info[0] for direction_info in all_direction_info_2D]
1122
+ ridge_directions = np.array([item for sublist in ridge_directions for item in sublist])
1123
+
1124
+ flattened_all_vertices_2D = np.vstack((ridge_inices,another_vertex_inices,ridge_lengths,ridge_widths,ridge_angles,ridge_directions))
1125
+
1126
+ # Convert ragged info list to a rectangular shape numpy array
1127
+ max_wings = max([vertex_info[0] for vertex_info in all_vertices_info_2D])
1128
+
1129
+ all_vertices_info_2D_nparray = np.zeros((npt_per_layer,1+max_wings*5))
1130
+ all_vertices_info_2D_nparray[:,0] = [vertex_info[0] for vertex_info in all_vertices_info_2D]
1131
+
1132
+ for i in range(0,npt_per_layer):
1133
+ nwings = all_vertices_info_2D[i][0]
1134
+ all_vertices_info_2D_nparray[i,1:nwings+1] = all_vertices_info_2D[i][1]
1135
+ all_vertices_info_2D_nparray[i,max_wings+1:max_wings+nwings+1] = all_vertices_info_2D[i][2]
1136
+ all_vertices_info_2D_nparray[i,2*max_wings+1:2*max_wings+nwings+1] = all_vertices_info_2D[i][3]
1137
+ all_vertices_info_2D_nparray[i,3*max_wings+1:3*max_wings+nwings+1] = all_vertices_info_2D[i][4]
1138
+ all_vertices_info_2D_nparray[i,4*max_wings+1:4*max_wings+nwings+1] = all_vertices_info_2D[i][5]
1139
+
1140
+ # Save info to txt files
1141
+ all_vertices_2D = np.hstack((all_pts_2D[0:npt_per_layer,:],all_vertices_info_2D_nparray))
1142
+ np.savetxt(Path('meshes/' + geoName + '/' + geoName +'-vertex.mesh'), all_vertices_2D, fmt='%.16g', delimiter=' '\
1143
+ ,header='Vertex Data Generated with Wood Mesh Generation Tool\n\
1144
+ Number of vertices\n'+ str(npt_per_layer) +
1145
+ '\n\
1146
+ Max number of wings for one vertex\n'+ str(max_wings) +
1147
+ '\n\
1148
+ [xcoord ycoord nwings ridge1 ... ridgen farvertex1 ... farvertexn length1 ... lengthn width1 ... widthn angle1 ... anglen]', comments='')
1149
+
1150
+ np.savetxt(Path('meshes/' + geoName + '/' + geoName +'-ridge.mesh'), all_ridges, fmt='%d', delimiter=' '\
1151
+ ,header='Ridge Data Generated with Wood Mesh Generation Tool\n\
1152
+ Number of ridges\n'+ str(nridge) +
1153
+ '\n\
1154
+ [vertex1 vertex2 midpt1 midpt2 qrtrpt1 qrtrpt2]', comments='')
1155
+
1156
+ return all_vertices_2D, max_wings, flattened_all_vertices_2D, all_ridges
1157
+
1158
+
1159
+ def GenerateBeamElement(NURBS_degree,nctrlpt_per_beam,fiberlength,theta_min,theta_max,\
1160
+ z_min,z_max,long_connector_ratio,npt_per_layer,voronoi_vertices,\
1161
+ nvertex,voronoi_ridges,nridge,log_center,all_vertices_2D,max_wings,\
1162
+ flattened_all_vertices_2D,all_ridges):
1163
+
1164
+
1165
+ nctrlpt_per_elem = NURBS_degree + 1
1166
+ nbeam_per_grain = int(round((z_max-z_min)/fiberlength))
1167
+
1168
+ nlayer = nctrlpt_per_beam*nbeam_per_grain
1169
+
1170
+ nconnector_t_per_beam = int((nctrlpt_per_beam-1)/NURBS_degree+1)
1171
+ nconnector_t_per_grain = int(nconnector_t_per_beam*nbeam_per_grain)
1172
+
1173
+ theta = np.linspace(theta_min,theta_max,nlayer-(nbeam_per_grain-1))
1174
+ z_coord = np.linspace(z_min,z_max,nlayer-(nbeam_per_grain-1))
1175
+ connector_l_length = fiberlength*long_connector_ratio
1176
+ connector_l_angle = (theta_max-theta_min)/nbeam_per_grain*long_connector_ratio
1177
+
1178
+ # Insert repeated layers for the longitudinal connectors
1179
+ for i in range(nlayer-(nbeam_per_grain-1)-(nctrlpt_per_beam-1),1,-(nctrlpt_per_beam-1)):
1180
+ theta = np.insert(theta,i,theta[i-1])
1181
+ theta[i:] += connector_l_angle
1182
+ for i in range(nlayer-(nbeam_per_grain-1)-(nctrlpt_per_beam-1),1,-(nctrlpt_per_beam-1)):
1183
+ z_coord = np.insert(z_coord,i,z_coord[i-1])
1184
+ z_coord[i:] += connector_l_length
1185
+
1186
+ # Data for IGA use
1187
+ vertices_new = np.zeros((nlayer,npt_per_layer,3))
1188
+ for i in range(0,nlayer):
1189
+ for j in range(0,npt_per_layer):
1190
+ vertices_new[i,j,:2] = rotate_around_point_highperf(voronoi_vertices[j,:], theta[i], log_center)
1191
+ vertices_new[i,j,2] = z_coord[i]
1192
+
1193
+ # Vertex Data for IGA
1194
+ woodIGAvertices = np.reshape(vertices_new,(-1,3))
1195
+
1196
+ # Connectivity for IGA
1197
+ npt_total = nlayer*npt_per_layer
1198
+ vertex_connectivity = np.linspace(0,npt_total-1,npt_total)
1199
+
1200
+ # Beams
1201
+ ngrain = nvertex
1202
+ nbeam_total = ngrain*nbeam_per_grain
1203
+ beam_connectivity_original = np.zeros((nbeam_total,nctrlpt_per_beam))
1204
+ for i in range(0,nbeam_per_grain):
1205
+ for j in range(0, ngrain):
1206
+ irow = i*ngrain + j
1207
+ ivertex = i*npt_per_layer*nctrlpt_per_beam + j
1208
+ for icol in range(0,nctrlpt_per_beam):
1209
+ beam_connectivity_original[irow,icol] = ivertex+icol*npt_per_layer
1210
+
1211
+ # Rearange beam connectivity such that each row is corresponding to a Bezier beam element
1212
+ beam_connectivity = np.copy(beam_connectivity_original)
1213
+ for ictrlpt in range(nctrlpt_per_beam-NURBS_degree,1,-NURBS_degree):
1214
+ beam_connectivity = np.insert(beam_connectivity,ictrlpt,beam_connectivity[:,ictrlpt-1],axis=1)
1215
+
1216
+ beam_connectivity_original = (beam_connectivity_original+1).astype(int) # +1 because of in abaqus index starts from 1
1217
+ beam_connectivity = (np.reshape(beam_connectivity,(-1,nctrlpt_per_elem))+1).astype(int) # +1 because of in abaqus index starts from 1
1218
+
1219
+
1220
+ nbeamElem = beam_connectivity.shape[0]
1221
+
1222
+
1223
+ # Transverse connectors
1224
+ nconnector_t = nridge*nconnector_t_per_grain
1225
+ connector_t_connectivity = np.zeros((nconnector_t,2))
1226
+ for i in range(0,nbeam_per_grain):
1227
+ for j in range(0,nconnector_t_per_beam):
1228
+ for k in range(0,nridge):
1229
+ irow = i*nconnector_t_per_beam*nridge + j*nridge + k
1230
+ connector_t_connectivity[irow,:] = voronoi_ridges[k]+i*npt_per_layer*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer
1231
+
1232
+ connector_t_connectivity = (connector_t_connectivity+1).astype(int)
1233
+ # Slicing block indices, https://stackoverflow.com/questions/39692769/efficient-numpy-indexing-take-first-n-rows-of-every-block-of-m-rows
1234
+ connector_t_index = np.linspace(0,nconnector_t-1,nconnector_t).astype(int)
1235
+ connector_t_bot_index = connector_t_index[np.mod(np.arange(connector_t_index.size),nridge*nconnector_t_per_beam)<nridge]
1236
+ connector_t_top_index = connector_t_index[np.mod(np.arange(connector_t_index.size),nridge*nconnector_t_per_beam)>=(nconnector_t_per_beam-1)*nridge]
1237
+ connector_t_reg_index = np.setdiff1d(np.setdiff1d(connector_t_index, connector_t_bot_index),connector_t_top_index)
1238
+ connector_t_bot_connectivity = np.copy(connector_t_connectivity)[connector_t_bot_index,:]
1239
+ connector_t_top_connectivity = np.copy(connector_t_connectivity)[connector_t_top_index,:]
1240
+ connector_t_reg_connectivity = np.copy(connector_t_connectivity)[connector_t_reg_index,:]
1241
+
1242
+
1243
+ # Longitudinal connectors
1244
+ nwings = all_vertices_2D[:,2].astype(int)
1245
+ nconnector_l_per_layer = sum(nwings)
1246
+ nconnector_l = nconnector_l_per_layer * (nbeam_per_grain-1)
1247
+ connector_l_connectivity = np.zeros((nconnector_l,2))
1248
+ connector_l_vertex_dict = np.zeros(nconnector_l)
1249
+ irow_conn_l = 0
1250
+ for i in range(0,nbeam_per_grain-1): # loop over layers of longitudinal connectors
1251
+ for j in range(0,ngrain): # loop over grains in each layer
1252
+ n = nwings[j]
1253
+ irow = i*ngrain + j
1254
+ for k in range(0,n): # loop over wings of each grain
1255
+ connector_l_connectivity[irow_conn_l,:] = (beam_connectivity_original[irow,-1],beam_connectivity_original[irow,-1]+npt_per_layer)
1256
+ connector_l_vertex_dict[irow_conn_l] = irow
1257
+ irow_conn_l += 1
1258
+
1259
+ connector_l_vertex_dict = connector_l_vertex_dict.astype(int)
1260
+ connector_l_connectivity = connector_l_connectivity.astype(int)
1261
+
1262
+ nconnector_total = nconnector_t + nconnector_l
1263
+
1264
+ return woodIGAvertices,vertex_connectivity,beam_connectivity_original,nbeam_total,\
1265
+ beam_connectivity,nbeamElem,nlayer,connector_t_connectivity,\
1266
+ connector_t_bot_connectivity,connector_t_top_connectivity,\
1267
+ connector_t_reg_connectivity,connector_l_connectivity,nconnector_t_per_beam,\
1268
+ nconnector_t_per_grain,nconnector_t,nconnector_l,nconnector_total,\
1269
+ theta,z_coord,nbeam_per_grain,connector_l_vertex_dict
1270
+
1271
+
1272
+ def ConnectorMeshFile(geoName,woodIGAvertices,connector_t_bot_connectivity,\
1273
+ connector_t_reg_connectivity,connector_t_top_connectivity,\
1274
+ height_connector_t,connector_l_connectivity,all_vertices_2D,\
1275
+ max_wings,flattened_all_vertices_2D,nbeam_per_grain,nridge,\
1276
+ connector_l_vertex_dict):
1277
+ ######### txt File stores the connector data for Abaqus analyses ##############
1278
+ nelem_connector_t_bot = connector_t_bot_connectivity.shape[0]
1279
+ nelem_connector_t_reg = connector_t_reg_connectivity.shape[0]
1280
+ nelem_connector_t_top = connector_t_top_connectivity.shape[0]
1281
+ nelem_connector_l = connector_l_connectivity.shape[0]
1282
+ nelem_total = nelem_connector_t_bot + nelem_connector_t_reg + nelem_connector_t_top + nelem_connector_l
1283
+
1284
+ conn_l_ridge_index = flattened_all_vertices_2D[0,:].astype(int)
1285
+ conn_l_lengths = flattened_all_vertices_2D[2,:]
1286
+ # conn_l_widths = flattened_all_vertices_2D[3,:]
1287
+ conn_l_angles = flattened_all_vertices_2D[4,:]
1288
+ rot = np.array([[np.cos(conn_l_angles), -np.sin(conn_l_angles)], [np.sin(conn_l_angles), np.cos(conn_l_angles)]]).T
1289
+ rot = np.hstack((rot[:,0,:],np.zeros(len(conn_l_angles))[:, np.newaxis])) # convert to 3D rotation matrix, assumes rotation remains still in-plane
1290
+ conn_l_tangents = rot
1291
+
1292
+ # Meshdata = [nodex1 nodey1 nodez1 nodex2 nodey2 nodez2 centerx centery centerz dx1 dy1 dz1 dx2 dy2 dz2 width height]
1293
+ Meshdata = np.zeros((nelem_total,17))
1294
+
1295
+ for i in range(0,nelem_connector_t_bot):
1296
+ Meshdata[i,0:3] = np.copy(woodIGAvertices)[connector_t_bot_connectivity[i,0]-1,:]
1297
+ Meshdata[i,3:6] = np.copy(woodIGAvertices)[connector_t_bot_connectivity[i,1]-1,:]
1298
+ Meshdata[i,16] = height_connector_t
1299
+ for i in range(0,nelem_connector_t_reg):
1300
+ Meshdata[i+nelem_connector_t_bot,0:3] = np.copy(woodIGAvertices)[connector_t_reg_connectivity[i,0]-1,:]
1301
+ Meshdata[i+nelem_connector_t_bot,3:6] = np.copy(woodIGAvertices)[connector_t_reg_connectivity[i,1]-1,:]
1302
+ Meshdata[i+nelem_connector_t_bot,16] = height_connector_t*2
1303
+ for i in range(0,nelem_connector_t_top):
1304
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,0:3] = np.copy(woodIGAvertices)[connector_t_top_connectivity[i,0]-1,:]
1305
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,3:6] = np.copy(woodIGAvertices)[connector_t_top_connectivity[i,1]-1,:]
1306
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,16] = height_connector_t
1307
+
1308
+ for i in range(0,nelem_connector_l):
1309
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,0:3] = np.copy(woodIGAvertices)[connector_l_connectivity[i,0]-1,:]
1310
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,3:6] = np.copy(woodIGAvertices)[connector_l_connectivity[i,1]-1,:]
1311
+ Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,16] = conn_l_lengths[i]
1312
+
1313
+ Meshdata[:,6:9] = (Meshdata[:,0:3] + Meshdata[:,3:6])/2
1314
+ Meshdata[0:nelem_connector_t_bot,8] = Meshdata[0:nelem_connector_t_bot,8]
1315
+ Meshdata[nelem_total-nelem_connector_t_top:nelem_total,8] = Meshdata[nelem_total-nelem_connector_t_top:nelem_total,8]
1316
+ Meshdata[:,9:12] = Meshdata[:,6:9] - Meshdata[:,0:3]
1317
+ Meshdata[:,12:15] = Meshdata[:,6:9] - Meshdata[:,3:6]
1318
+
1319
+ # Add z-variation/random field for cellwall thicknesses/connector widths
1320
+ for i in range(0,nridge):
1321
+ Meshdata[i,15] = all_vertices_2D[connector_t_bot_connectivity[i,1]-1,3+max_wings*3] # assign widths to all bot transverse connectors
1322
+ Meshdata[:nelem_total-nelem_connector_l,15] = np.tile(Meshdata[0:nridge,15],3*nbeam_per_grain) # use the same widths for reg and top transverse connectors
1323
+
1324
+ conn_l_widths = Meshdata[conn_l_ridge_index,15]
1325
+ Meshdata[nelem_total-nelem_connector_l:nelem_total,15] = conn_l_widths
1326
+
1327
+ # add the eccentricity to the centers for longitudinal connectors (new center is correct)
1328
+ Meshdata[nelem_total-nelem_connector_l:nelem_total,6:9] += conn_l_tangents*Meshdata[nelem_total-nelem_connector_l:nelem_total,16][:, np.newaxis]/2
1329
+
1330
+ # Replace nodal coordinates with nodal indices
1331
+ Meshdata[:,0:2] = np.concatenate((connector_t_bot_connectivity,connector_t_reg_connectivity,connector_t_top_connectivity,connector_l_connectivity))
1332
+ Meshdata = np.delete(Meshdata,[2,3,4,5],1)
1333
+
1334
+ np.savetxt(Path('meshes/' + geoName + '/' + geoName+'-mesh.txt'), Meshdata, fmt='%.16g', delimiter=' '\
1335
+ ,header='# Connector Data Generated with Wood Mesh Generation Tool\n\
1336
+ Number of bot connectors\n'+ str(nelem_connector_t_bot) +
1337
+ '\n\
1338
+ Number of reg connectors\n'+ str(nelem_connector_t_reg) +
1339
+ '\n\
1340
+ Number of top connectors\n'+ str(nelem_connector_t_top) +
1341
+ '\n\
1342
+ Number of long connectors\n'+ str(nelem_connector_l) +
1343
+ '\n\
1344
+ [inode jnode centerx centery centerz dx1 dy1 dz1 dx2 dy2 dz2 width height]', comments='')
1345
+
1346
+ return Meshdata
1347
+
1348
+
1349
+ def insert_precracks(all_pts_2D,all_ridges,nridge,npt_per_layer,\
1350
+ npt_per_layer_normal,npt_per_layer_vtk,\
1351
+ nlayer,precrack_nodes,precrack_widths,\
1352
+ cellsize_early,):
1353
+
1354
+ precrack_midpts = (precrack_nodes[:,0:2]+precrack_nodes[:,2:4])/2.0
1355
+ ridge_midpts = all_pts_2D[all_ridges[:,2]]
1356
+ ridge_midpts_tree = cKDTree(ridge_midpts)
1357
+ near_ridges = []
1358
+ for midpt in precrack_midpts:
1359
+ near_ridges.append(ridge_midpts_tree.query_ball_point(midpt,3*cellsize_early))
1360
+
1361
+ # Find the intersect point of neighboring ridges (lines) with the precrack line
1362
+ precracked_elem = []
1363
+ for i in range(0,len(precrack_nodes)):
1364
+ p = precrack_nodes[i,0:2]
1365
+ normal = (precrack_nodes[i,2:4] - p)/np.linalg.norm(precrack_nodes[i,2:4] - p)
1366
+ for ridge in near_ridges[i]:
1367
+ q = all_pts_2D[all_ridges[ridge,0],:]
1368
+ s = all_pts_2D[all_ridges[ridge,1],:] - q
1369
+ u = np.cross((q-p),normal)/np.cross(normal,s)
1370
+ if (u >= 0) and (u <= 1):
1371
+ precracked_elem.append(ridge)
1372
+
1373
+ # Visualize the precracks in the preview plot
1374
+ plt.plot(precrack_nodes[0,0::2],precrack_nodes[0,1::2],'r-',linewidth=2)
1375
+ return precracked_elem
1376
+
1377
+
1378
+ def VisualizationFiles(geoName,NURBS_degree,nlayer,npt_per_layer_vtk,all_pts_2D,\
1379
+ fiberlength,theta,z_coord,nbeam_per_grain,nridge,\
1380
+ voronoi_ridges,log_center,all_ridges,nvertex,\
1381
+ nconnector_t,nconnector_l,nctrlpt_per_beam,ConnMeshData,\
1382
+ all_vertices_2D,max_wings,flattened_all_vertices_2D):
1383
+ ngrain = nvertex
1384
+ ninterval_per_beam_vtk = int((nctrlpt_per_beam-1)/2) # 2 layers ----> 1 interval
1385
+
1386
+ nconnector_t_per_beam = int((nctrlpt_per_beam-1)/NURBS_degree+1)
1387
+
1388
+ # Data for VTK use
1389
+ vertices_new = np.zeros((nlayer,npt_per_layer_vtk,3))
1390
+ for i in range(0,nlayer):
1391
+ for j in range(0,npt_per_layer_vtk):
1392
+ vertices_new[i,j,:2] = rotate_around_point_highperf(all_pts_2D[j,:], theta[i], log_center)
1393
+ vertices_new[i,j,2] = z_coord[i]
1394
+
1395
+ # Vertex Data for VTK use
1396
+ woodVTKvertices = np.reshape(vertices_new,(-1,3))
1397
+
1398
+ # Connectivity for VTK use
1399
+ # Vertex
1400
+ npt_total_vtk = nlayer*npt_per_layer_vtk
1401
+ vertex_connectivity_vtk = np.linspace(0,npt_total_vtk-1,npt_total_vtk)
1402
+
1403
+ # Beam
1404
+ nbeam_total_vtk = ngrain*nbeam_per_grain*ninterval_per_beam_vtk
1405
+ beam_connectivity_vtk = np.zeros((nbeam_total_vtk,3)) # 3 is the number of points per beam in Paraview
1406
+ for i in range(0,nbeam_per_grain):
1407
+ for j in range(0,ninterval_per_beam_vtk):
1408
+ for k in range(0,ngrain):
1409
+ irow = i*ninterval_per_beam_vtk*ngrain + j*ngrain + k
1410
+ ivertex = (i + (i*ninterval_per_beam_vtk+j)*2)*npt_per_layer_vtk + k
1411
+ beam_connectivity_vtk[irow,:] = (ivertex,ivertex+2*npt_per_layer_vtk,ivertex+npt_per_layer_vtk)
1412
+
1413
+ # Transverse connectors
1414
+ connector_t_connectivity_vtk = np.zeros((nconnector_t,2))
1415
+ for i in range(0,nbeam_per_grain):
1416
+ for j in range(0,nconnector_t_per_beam):
1417
+ for k in range(0,nridge):
1418
+ irow = i*nconnector_t_per_beam*nridge + j*nridge + k
1419
+ connector_t_connectivity_vtk[irow,:] = (voronoi_ridges[k][0]+i*npt_per_layer_vtk*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer_vtk,voronoi_ridges[k][1]+i*npt_per_layer_vtk*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer_vtk)
1420
+
1421
+ # Longitudinal connectors
1422
+ connector_l_connectivity_vtk = np.zeros((nconnector_l,2))
1423
+ nwings = all_vertices_2D[:,2].astype(int)
1424
+ irow_conn_l = 0
1425
+ for i in range(0,nbeam_per_grain-1): # loop over layers of longitudinal connectors
1426
+ for j in range(0,ngrain): # loop over grains in each layer
1427
+ nw = nwings[j]
1428
+ irow = i*ngrain + j
1429
+ for k in range(0,nw): # loop over wings of each grain
1430
+ connector_l_connectivity_vtk[irow_conn_l,:] = (beam_connectivity_vtk[irow+(ninterval_per_beam_vtk-1)*ngrain,1],beam_connectivity_vtk[irow+(ninterval_per_beam_vtk-1)*ngrain,1]+npt_per_layer_vtk)
1431
+ irow_conn_l += 1
1432
+
1433
+ # Quad - Beam Wings
1434
+ nquad = nridge*2
1435
+ nquad_total = nquad*nbeam_per_grain*ninterval_per_beam_vtk
1436
+ quad_connectivity = np.zeros((nquad_total,8))
1437
+ for i in range(0,nbeam_per_grain):
1438
+ for j in range(0,ninterval_per_beam_vtk):
1439
+ for k in range(0,nridge):
1440
+ for l in range(0,2):
1441
+ irow = i*ninterval_per_beam_vtk*nquad + j*nquad + l*nridge + k
1442
+ ivertex = i + (i*ninterval_per_beam_vtk+j)*2
1443
+ quad_connectivity[irow,:] = (all_ridges[k][l]+ivertex*npt_per_layer_vtk,all_ridges[k][l]+(ivertex+2)*npt_per_layer_vtk, \
1444
+ all_ridges[k][l+2]+(ivertex+2)*npt_per_layer_vtk,all_ridges[k][l+2]+ivertex*npt_per_layer_vtk, \
1445
+ all_ridges[k][l]+(ivertex+1)*npt_per_layer_vtk,all_ridges[k][l+4]+(ivertex+2)*npt_per_layer_vtk, \
1446
+ all_ridges[k][l+2]+(ivertex+1)*npt_per_layer_vtk,all_ridges[k][l+4]+ivertex*npt_per_layer_vtk)
1447
+
1448
+ # Quad - Connector Cross-sections
1449
+ nquad_conn = nconnector_t + nconnector_l
1450
+ nquad_conn_total = nquad_conn
1451
+
1452
+ conn_l_angles = flattened_all_vertices_2D[4,:]
1453
+
1454
+ Quad_center = np.copy(ConnMeshData[:,2:5])
1455
+ Quad_normal1 = np.copy(ConnMeshData[:,5:8])
1456
+ Quad_normal2 = np.copy(ConnMeshData[:,8:11])
1457
+ Quad_length1 = np.linalg.norm(Quad_normal1, axis=1)
1458
+ Quad_length2 = np.linalg.norm(Quad_normal2, axis=1)
1459
+ Quad_width = np.copy(ConnMeshData[:,11])
1460
+ Quad_height = np.copy(ConnMeshData[:,12])
1461
+ Quad_normal = Quad_normal1/Quad_length1[:, np.newaxis]
1462
+ Quad_tangent = np.zeros((nquad_conn_total,3))
1463
+ Quad_tangent[0:nconnector_t] = np.tile(np.array([0.0,0.0,1.0]),(nconnector_t,1)) # assume a tangent of (0,0,1) for all conn_t
1464
+
1465
+ rot = np.array([[np.cos(conn_l_angles), -np.sin(conn_l_angles)], [np.sin(conn_l_angles), np.cos(conn_l_angles)]]).T
1466
+ rot = np.hstack((rot[:,0,:],np.zeros(len(conn_l_angles))[:, np.newaxis])) # convert to 3D rotation matrix, assumes rotation remains still in-plane
1467
+ Quad_tangent[nconnector_t:nquad_conn] = rot
1468
+ Quad_bitangent = np.cross(Quad_normal,Quad_tangent)
1469
+
1470
+ nconnector_t_bot = int(nconnector_t/3)
1471
+ nconnector_t_top = int(nconnector_t/3)
1472
+ # Add eccentricities to bot/top transverse connectors
1473
+ Quad_center[0:nconnector_t_bot,:] = Quad_center[0:nconnector_t_bot,:] + Quad_tangent[0:nconnector_t_bot,:]*Quad_height[0:nconnector_t_bot,np.newaxis]/2 # bot connectors
1474
+ Quad_center[(nconnector_t-nconnector_t_top):nconnector_t,:] = Quad_center[(nconnector_t-nconnector_t_top):nconnector_t,:] - Quad_tangent[(nconnector_t-nconnector_t_top):nconnector_t,:]*Quad_height[(nconnector_t-nconnector_t_top):nconnector_t,np.newaxis]/2
1475
+
1476
+ quad_conn_vertices = np.zeros((nquad_conn_total*4,3))
1477
+ quad_conn_vertices[0:nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
1478
+ quad_conn_vertices[nquad_conn_total:2*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
1479
+ quad_conn_vertices[2*nquad_conn_total:3*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
1480
+ quad_conn_vertices[3*nquad_conn_total:4*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
1481
+
1482
+ quad_conn_connectivity = np.linspace(0,nquad_conn_total*4-1,nquad_conn_total*4)
1483
+ quad_conn_connectivity = np.reshape(quad_conn_connectivity, [4,-1]).T
1484
+ quad_conn_connectivity = quad_conn_connectivity + npt_total_vtk
1485
+
1486
+ # Modify Vertex Data by adding quad_conn_vertices
1487
+ woodVTKvertices_quad_conn = np.vstack((woodVTKvertices,quad_conn_vertices))
1488
+ npt_total_vtk_quad_conn = woodVTKvertices_quad_conn.shape[0]
1489
+ vertex_connectivity_vtk_quad_conn = np.linspace(0,npt_total_vtk_quad_conn-1,npt_total_vtk_quad_conn)
1490
+
1491
+ # Hex - Connector Volumes
1492
+ nhex_conn_total = nquad_conn_total*2
1493
+
1494
+ hex_conn_vertices = np.zeros((nquad_conn_total*4*3,3))
1495
+ hex_conn_vertices[0:nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
1496
+ hex_conn_vertices[nquad_conn_total:2*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
1497
+ hex_conn_vertices[2*nquad_conn_total:3*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
1498
+ hex_conn_vertices[3*nquad_conn_total:4*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
1499
+
1500
+ hex_conn_vertices[4*nquad_conn_total:5*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
1501
+ hex_conn_vertices[5*nquad_conn_total:6*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
1502
+ hex_conn_vertices[6*nquad_conn_total:7*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
1503
+ hex_conn_vertices[7*nquad_conn_total:8*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
1504
+
1505
+ hex_conn_vertices[8*nquad_conn_total:9*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
1506
+ hex_conn_vertices[9*nquad_conn_total:10*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
1507
+ hex_conn_vertices[10*nquad_conn_total:11*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
1508
+ hex_conn_vertices[11*nquad_conn_total:12*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
1509
+
1510
+ hex_conn1_connectivity = np.linspace(0,nquad_conn_total*4*2-1,nquad_conn_total*4*2)
1511
+ hex_conn1_connectivity = np.reshape(hex_conn1_connectivity, [8,-1]).T
1512
+ hex_conn2_connectivity = np.linspace(0,nquad_conn_total*4*2-1,nquad_conn_total*4*2)
1513
+ hex_conn2_connectivity = np.reshape(hex_conn2_connectivity, [8,-1]).T
1514
+ hex_conn2_connectivity[:,4:8] = hex_conn2_connectivity[:,4:8] + nquad_conn_total*4
1515
+ hex_conn_connectivity = np.vstack((hex_conn1_connectivity,hex_conn2_connectivity))
1516
+ hex_conn_connectivity = hex_conn_connectivity + npt_total_vtk
1517
+
1518
+ # Modify Vertex Data by adding hex_conn_vertices
1519
+ woodVTKvertices_hex_conn = np.vstack((woodVTKvertices,hex_conn_vertices))
1520
+ npt_total_vtk_hex_conn = woodVTKvertices_hex_conn.shape[0]
1521
+ vertex_connectivity_vtk_hex_conn = np.linspace(0,npt_total_vtk_hex_conn-1,npt_total_vtk_hex_conn)
1522
+
1523
+ # =============================================================================
1524
+ # Paraview Visualization File
1525
+ woodVTKcell_types = np.concatenate((np.ones(npt_total_vtk),3*np.ones(nconnector_t),3*np.ones(nconnector_l),21*np.ones(nbeam_total_vtk),23*np.ones(nquad_total))).astype(int)
1526
+ ncell = woodVTKcell_types.shape[0]
1527
+
1528
+ collocation_flag_vtk = np.concatenate((np.ones(ngrain),np.zeros(npt_per_layer_vtk*NURBS_degree-ngrain)))
1529
+ collocation_flag_vtk = np.concatenate((np.tile(collocation_flag_vtk, nconnector_t_per_beam-1),np.concatenate((np.ones(ngrain),np.zeros(npt_per_layer_vtk-ngrain)))))
1530
+ collocation_flag_vtk = np.tile(collocation_flag_vtk, nbeam_per_grain)
1531
+
1532
+ vtkfile = open (Path('meshes/' + geoName + '/' + geoName + '_00001'+'.vtu'),'w')
1533
+
1534
+ vtkfile.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
1535
+ vtkfile.write('<UnstructuredGrid>'+'\n')
1536
+ vtkfile.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell)+'">'+'\n')
1537
+
1538
+ # <Points>
1539
+ vtkfile.write('<Points>'+'\n')
1540
+ vtkfile.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
1541
+ for i in range(0,npt_total_vtk):
1542
+ X,Y,Z = woodVTKvertices[i]
1543
+ vtkfile.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
1544
+ vtkfile.write('</DataArray>'+'\n')
1545
+ vtkfile.write('</Points>'+'\n')
1546
+ # </Points>
1547
+
1548
+
1549
+ # <PointData>
1550
+ vtkfile.write("<"+"PointData"\
1551
+ +" "+"Tensors="+'"'+""+'"'\
1552
+ +" "+"Vevtors="+'"'+""+'"'\
1553
+ +" "+"Scalars="+'"'+"IGAcollocation_flag"+'"'+">"+'\n')
1554
+
1555
+ # IGA collocation flag
1556
+ vtkfile.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"IGAcollocation_flag" format='+'"'+"ascii"+'"'+">"+'\n')
1557
+ for i in range(0,npt_total_vtk):
1558
+ X = collocation_flag_vtk[i]
1559
+ vtkfile.write('%11.8e'%X+'\n')
1560
+ vtkfile.write("</DataArray>"+'\n')
1561
+ vtkfile.write('</PointData>'+'\n')
1562
+ # </PointData>
1563
+
1564
+
1565
+ # <Cells>
1566
+ vtkfile.write('<Cells>'+'\n')
1567
+
1568
+ # Cell connectivity
1569
+ vtkfile.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
1570
+ for x in vertex_connectivity_vtk.astype(int):
1571
+ vtkfile.write("%s\n" % x)
1572
+ #vtkfile.write('\n')
1573
+ vtkfile.write("\n".join(" ".join(map(str, x)) for x in connector_t_connectivity_vtk.astype(int)))
1574
+ vtkfile.write('\n')
1575
+ vtkfile.write("\n".join(" ".join(map(str, x)) for x in connector_l_connectivity_vtk.astype(int)))
1576
+ vtkfile.write('\n')
1577
+ vtkfile.write("\n".join(" ".join(map(str, x)) for x in beam_connectivity_vtk.astype(int)))
1578
+ vtkfile.write('\n')
1579
+ vtkfile.write("\n".join(" ".join(map(str, x)) for x in quad_connectivity.astype(int)))
1580
+ vtkfile.write('\n')
1581
+ vtkfile.write('</DataArray>'+'\n')
1582
+
1583
+ # Cell offsets
1584
+ vtkfile.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
1585
+ current_offset = 0
1586
+ for element in vertex_connectivity_vtk:
1587
+ element_offset = 1
1588
+ current_offset += element_offset
1589
+ vtkfile.write(str(current_offset)+'\n')
1590
+ for element in connector_t_connectivity_vtk:
1591
+ element_offset = len(element)
1592
+ current_offset += element_offset
1593
+ vtkfile.write(str(current_offset)+'\n')
1594
+ for element in connector_l_connectivity_vtk:
1595
+ element_offset = len(element)
1596
+ current_offset += element_offset
1597
+ vtkfile.write(str(current_offset)+'\n')
1598
+ for element in beam_connectivity_vtk:
1599
+ element_offset = len(element)
1600
+ current_offset += element_offset
1601
+ vtkfile.write(str(current_offset)+'\n')
1602
+ for element in quad_connectivity:
1603
+ element_offset = len(element)
1604
+ current_offset += element_offset
1605
+ vtkfile.write(str(current_offset)+'\n')
1606
+ vtkfile.write('</DataArray>'+'\n')
1607
+
1608
+ # Cell type
1609
+ vtkfile.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
1610
+ for i in range(0,ncell):
1611
+ element = woodVTKcell_types[i]
1612
+ vtkfile.write(str(element)+'\n')
1613
+ vtkfile.write('</DataArray>'+'\n')
1614
+
1615
+ vtkfile.write('</Cells>'+'\n')
1616
+ # </Cells>
1617
+
1618
+ vtkfile.write('</Piece>'+'\n')
1619
+ #</Piece>
1620
+
1621
+ vtkfile.write('</UnstructuredGrid>'+'\n')
1622
+ #</UnstructuredGrid>
1623
+
1624
+ vtkfile.write('</VTKFile>'+'\n')
1625
+ #</VTKFile>
1626
+
1627
+ vtkfile.close()
1628
+
1629
+ # =============================================================================
1630
+ # Paraview Vertices File
1631
+ woodVTKcell_types_vertices = (np.ones(npt_total_vtk)).astype(int)
1632
+
1633
+ ncell_vertices = woodVTKcell_types_vertices.shape[0]
1634
+
1635
+ vtkfile_vertices = open (Path('meshes/' + geoName + '/' + geoName + '_vertices'+'.vtu'),'w')
1636
+
1637
+ vtkfile_vertices.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
1638
+ vtkfile_vertices.write('<UnstructuredGrid>'+'\n')
1639
+ vtkfile_vertices.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell_vertices)+'">'+'\n')
1640
+
1641
+ # <Points>
1642
+ vtkfile_vertices.write('<Points>'+'\n')
1643
+ vtkfile_vertices.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
1644
+ for i in range(0,npt_total_vtk):
1645
+ X,Y,Z = woodVTKvertices[i]
1646
+ vtkfile_vertices.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
1647
+ vtkfile_vertices.write('</DataArray>'+'\n')
1648
+ vtkfile_vertices.write('</Points>'+'\n')
1649
+ # </Points>
1650
+
1651
+ # <PointData>
1652
+ vtkfile_vertices.write("<"+"PointData"\
1653
+ +" "+"Tensors="+'"'+""+'"'\
1654
+ +" "+"Vevtors="+'"'+""+'"'\
1655
+ +" "+"Scalars="+'"'+"IGAcollocation_flag"+'"'+">"+'\n')
1656
+
1657
+ # Point Data
1658
+ vtkfile_vertices.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"IGAcollocation_flag" format='+'"'+"ascii"+'"'+">"+'\n')
1659
+ for i in range(0,npt_total_vtk):
1660
+ X = collocation_flag_vtk[i]
1661
+ vtkfile_vertices.write('%11.8e'%X+'\n')
1662
+ vtkfile_vertices.write("</DataArray>"+'\n')
1663
+
1664
+ vtkfile_vertices.write('</PointData>'+'\n')
1665
+ # </PointData>
1666
+
1667
+
1668
+ # <Cells>
1669
+ vtkfile_vertices.write('<Cells>'+'\n')
1670
+
1671
+ # Cell connectivity
1672
+ vtkfile_vertices.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
1673
+ for x in vertex_connectivity_vtk.astype(int):
1674
+ vtkfile_vertices.write("%s\n" % x)
1675
+ vtkfile_vertices.write('\n')
1676
+ vtkfile_vertices.write('</DataArray>'+'\n')
1677
+
1678
+ # Cell offsets
1679
+ vtkfile_vertices.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
1680
+ current_offset = 0
1681
+ for element in vertex_connectivity_vtk:
1682
+ element_offset = 1
1683
+ current_offset += element_offset
1684
+ vtkfile_vertices.write(str(current_offset)+'\n')
1685
+ vtkfile_vertices.write('</DataArray>'+'\n')
1686
+
1687
+ # Cell type
1688
+ vtkfile_vertices.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
1689
+ for i in range(0,ncell_vertices):
1690
+ element = woodVTKcell_types_vertices[i]
1691
+ vtkfile_vertices.write(str(element)+'\n')
1692
+
1693
+ vtkfile_vertices.write('</DataArray>'+'\n')
1694
+
1695
+ vtkfile_vertices.write('</Cells>'+'\n')
1696
+ # </Cells>
1697
+
1698
+ vtkfile_vertices.write('</Piece>'+'\n')
1699
+ #</Piece>
1700
+
1701
+ vtkfile_vertices.write('</UnstructuredGrid>'+'\n')
1702
+ #</UnstructuredGrid>
1703
+
1704
+ vtkfile_vertices.write('</VTKFile>'+'\n')
1705
+ #</VTKFile>
1706
+
1707
+ vtkfile_vertices.close()
1708
+
1709
+ # =============================================================================
1710
+ # Paraview Beam File
1711
+ woodVTKcell_types_beams = np.concatenate((21*np.ones(nbeam_total_vtk),23*np.ones(nquad_total))).astype(int)
1712
+
1713
+ ncell_beams = woodVTKcell_types_beams.shape[0]
1714
+
1715
+ vtkfile_beams = open (Path('meshes/' + geoName + '/' + geoName + '_beams'+'.vtu'),'w')
1716
+
1717
+ vtkfile_beams.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
1718
+ vtkfile_beams.write('<UnstructuredGrid>'+'\n')
1719
+ vtkfile_beams.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell_beams)+'">'+'\n')
1720
+
1721
+ # <Points>
1722
+ vtkfile_beams.write('<Points>'+'\n')
1723
+ vtkfile_beams.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
1724
+ for i in range(0,npt_total_vtk):
1725
+ X,Y,Z = woodVTKvertices[i]
1726
+ vtkfile_beams.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
1727
+ vtkfile_beams.write('</DataArray>'+'\n')
1728
+ vtkfile_beams.write('</Points>'+'\n')
1729
+ # </Points>
1730
+
1731
+ # <PointData>
1732
+ # </PointData>
1733
+
1734
+ # <Cells>
1735
+ vtkfile_beams.write('<Cells>'+'\n')
1736
+
1737
+ # Cell connectivity
1738
+ vtkfile_beams.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
1739
+ vtkfile_beams.write("\n".join(" ".join(map(str, x)) for x in beam_connectivity_vtk.astype(int)))
1740
+ vtkfile_beams.write('\n')
1741
+ vtkfile_beams.write("\n".join(" ".join(map(str, x)) for x in quad_connectivity.astype(int)))
1742
+ vtkfile_beams.write('\n')
1743
+ vtkfile_beams.write('</DataArray>'+'\n')
1744
+
1745
+ # Cell offsets
1746
+ vtkfile_beams.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
1747
+ current_offset = 0
1748
+ for element in beam_connectivity_vtk:
1749
+ element_offset = len(element)
1750
+ current_offset += element_offset
1751
+ vtkfile_beams.write(str(current_offset)+'\n')
1752
+ for element in quad_connectivity:
1753
+ element_offset = len(element)
1754
+ current_offset += element_offset
1755
+ vtkfile_beams.write(str(current_offset)+'\n')
1756
+ vtkfile_beams.write('</DataArray>'+'\n')
1757
+
1758
+ # Cell type
1759
+ vtkfile_beams.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
1760
+ for i in range(0,ncell_beams):
1761
+ element = woodVTKcell_types_beams[i]
1762
+ vtkfile_beams.write(str(element)+'\n')
1763
+
1764
+ vtkfile_beams.write('</DataArray>'+'\n')
1765
+
1766
+ vtkfile_beams.write('</Cells>'+'\n')
1767
+ # </Cells>
1768
+
1769
+ vtkfile_beams.write('</Piece>'+'\n')
1770
+ #</Piece>
1771
+
1772
+ vtkfile_beams.write('</UnstructuredGrid>'+'\n')
1773
+ #</UnstructuredGrid>
1774
+
1775
+ vtkfile_beams.write('</VTKFile>'+'\n')
1776
+ #</VTKFile>
1777
+
1778
+ vtkfile_beams.close()
1779
+
1780
+ # =============================================================================
1781
+ # Paraview Connector (Axis + Center section) File
1782
+ woodVTKcell_types_conns = np.concatenate((3*np.ones(nconnector_t),3*np.ones(nconnector_l),9*np.ones(nconnector_t),9*np.ones(nconnector_l))).astype(int)
1783
+ ncell_conns = woodVTKcell_types_conns.shape[0]
1784
+
1785
+ Quad_width_vtk = np.tile(Quad_width,(2))
1786
+
1787
+ vtkfile_conns = open (Path('meshes/' + geoName + '/' + geoName + '_conns'+'.vtu'),'w')
1788
+
1789
+ vtkfile_conns.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
1790
+ vtkfile_conns.write('<UnstructuredGrid>'+'\n')
1791
+ vtkfile_conns.write('<Piece NumberOfPoints="'+str(npt_total_vtk_quad_conn)+'"'+' '+'NumberOfCells="'+str(ncell_conns)+'">'+'\n')
1792
+
1793
+ # <Points>
1794
+ vtkfile_conns.write('<Points>'+'\n')
1795
+ vtkfile_conns.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
1796
+ for i in range(0,npt_total_vtk_quad_conn):
1797
+ X,Y,Z = woodVTKvertices_quad_conn[i]
1798
+ vtkfile_conns.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
1799
+ vtkfile_conns.write('</DataArray>'+'\n')
1800
+ vtkfile_conns.write('</Points>'+'\n')
1801
+ # </Points>
1802
+
1803
+ # <PointData>
1804
+ # </PointData>
1805
+
1806
+ # <Cells>
1807
+ vtkfile_conns.write('<Cells>'+'\n')
1808
+
1809
+ # Cell connectivity
1810
+ vtkfile_conns.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
1811
+ vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in connector_t_connectivity_vtk.astype(int)))
1812
+ vtkfile_conns.write('\n')
1813
+ vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in connector_l_connectivity_vtk.astype(int)))
1814
+ vtkfile_conns.write('\n')
1815
+ vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in quad_conn_connectivity.astype(int)))
1816
+ vtkfile_conns.write('\n')
1817
+ vtkfile_conns.write('</DataArray>'+'\n')
1818
+
1819
+ # Cell offsets
1820
+ vtkfile_conns.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
1821
+ current_offset = 0
1822
+ for element in connector_t_connectivity_vtk:
1823
+ element_offset = len(element)
1824
+ current_offset += element_offset
1825
+ vtkfile_conns.write(str(current_offset)+'\n')
1826
+ for element in connector_l_connectivity_vtk:
1827
+ element_offset = len(element)
1828
+ current_offset += element_offset
1829
+ vtkfile_conns.write(str(current_offset)+'\n')
1830
+ for element in quad_conn_connectivity:
1831
+ element_offset = len(element)
1832
+ current_offset += element_offset
1833
+ vtkfile_conns.write(str(current_offset)+'\n')
1834
+ vtkfile_conns.write('</DataArray>'+'\n')
1835
+
1836
+ # Cell type
1837
+ vtkfile_conns.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
1838
+ for i in range(0,ncell_conns):
1839
+ element = woodVTKcell_types_conns[i]
1840
+ vtkfile_conns.write(str(element)+'\n')
1841
+
1842
+ vtkfile_conns.write('</DataArray>'+'\n')
1843
+
1844
+ vtkfile_conns.write('</Cells>'+'\n')
1845
+ # </Cells>
1846
+
1847
+ # <CellData>
1848
+ vtkfile_conns.write("<"+"CellData"\
1849
+ +" "+"Tensors="+'"'+""+'"'\
1850
+ +" "+"Vevtors="+'"'+""+'"'\
1851
+ +" "+"Scalars="+'"'+"Connector_width"+'"'+">"+'\n')
1852
+
1853
+ vtkfile_conns.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"Connector_width"'+" "+"format="+'"'+"ascii"+'"'+">"+'\n')
1854
+ for i in range(0,ncell_conns):
1855
+ X = Quad_width_vtk[i]
1856
+ vtkfile_conns.write('%11.8e'%X+'\n')
1857
+ vtkfile_conns.write('</DataArray>'+'\n')
1858
+
1859
+ vtkfile_conns.write('</CellData>'+'\n')
1860
+
1861
+ vtkfile_conns.write('</Piece>'+'\n')
1862
+ #</Piece>
1863
+
1864
+ vtkfile_conns.write('</UnstructuredGrid>'+'\n')
1865
+ #</UnstructuredGrid>
1866
+
1867
+ vtkfile_conns.write('</VTKFile>'+'\n')
1868
+ #</VTKFile>
1869
+
1870
+ vtkfile_conns.close()
1871
+
1872
+ # =============================================================================
1873
+ # Paraview Connector (Volume) File
1874
+ woodVTKcell_types_conns_vol = np.concatenate((12*np.ones(nconnector_t),12*np.ones(nconnector_l),12*np.ones(nconnector_t),12*np.ones(nconnector_l))).astype(int)
1875
+ ncell_conns_vol = woodVTKcell_types_conns_vol.shape[0]
1876
+
1877
+ Quad_width_vtk = np.tile(Quad_width,(2))
1878
+
1879
+ vtkfile_conns_vol = open (Path('meshes/' + geoName + '/' + geoName + '_conns_vol'+'.vtu'),'w')
1880
+
1881
+ vtkfile_conns_vol.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
1882
+ vtkfile_conns_vol.write('<UnstructuredGrid>'+'\n')
1883
+ vtkfile_conns_vol.write('<Piece NumberOfPoints="'+str(npt_total_vtk_hex_conn)+'"'+' '+'NumberOfCells="'+str(ncell_conns_vol)+'">'+'\n')
1884
+
1885
+ # <Points>
1886
+ vtkfile_conns_vol.write('<Points>'+'\n')
1887
+ vtkfile_conns_vol.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
1888
+ for i in range(0,npt_total_vtk_hex_conn):
1889
+ X,Y,Z = woodVTKvertices_hex_conn[i]
1890
+ vtkfile_conns_vol.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
1891
+ vtkfile_conns_vol.write('</DataArray>'+'\n')
1892
+ vtkfile_conns_vol.write('</Points>'+'\n')
1893
+ # </Points>
1894
+
1895
+ # <PointData>
1896
+ # </PointData>
1897
+
1898
+ # <Cells>
1899
+ vtkfile_conns_vol.write('<Cells>'+'\n')
1900
+
1901
+ # Cell connectivity
1902
+ vtkfile_conns_vol.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
1903
+ vtkfile_conns_vol.write("\n".join(" ".join(map(str, x)) for x in hex_conn_connectivity.astype(int)))
1904
+ vtkfile_conns_vol.write('\n')
1905
+ vtkfile_conns_vol.write('</DataArray>'+'\n')
1906
+
1907
+ # Cell offsets
1908
+ vtkfile_conns_vol.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
1909
+ current_offset = 0
1910
+ for element in hex_conn_connectivity:
1911
+ element_offset = len(element)
1912
+ current_offset += element_offset
1913
+ vtkfile_conns_vol.write(str(current_offset)+'\n')
1914
+ vtkfile_conns_vol.write('</DataArray>'+'\n')
1915
+
1916
+ # Cell type
1917
+ vtkfile_conns_vol.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
1918
+ for i in range(0,ncell_conns_vol):
1919
+ element = woodVTKcell_types_conns_vol[i]
1920
+ vtkfile_conns_vol.write(str(element)+'\n')
1921
+
1922
+ vtkfile_conns_vol.write('</DataArray>'+'\n')
1923
+
1924
+ vtkfile_conns_vol.write('</Cells>'+'\n')
1925
+ # </Cells>
1926
+
1927
+ # <CellData>
1928
+ vtkfile_conns_vol.write("<"+"CellData"\
1929
+ +" "+"Tensors="+'"'+""+'"'\
1930
+ +" "+"Vectors="+'"'+""+'"'\
1931
+ +" "+"Scalars="+'"'+"Connector_width"+'"'+">"+'\n')
1932
+
1933
+ vtkfile_conns_vol.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"Connector_width"'+" "+"format="+'"'+"ascii"+'"'+">"+'\n')
1934
+ for i in range(0,ncell_conns_vol):
1935
+ X = Quad_width_vtk[i]
1936
+ vtkfile_conns_vol.write('%11.8e'%X+'\n')
1937
+ vtkfile_conns_vol.write('</DataArray>'+'\n')
1938
+
1939
+ vtkfile_conns_vol.write('</CellData>'+'\n')
1940
+
1941
+ vtkfile_conns_vol.write('</Piece>'+'\n')
1942
+ #</Piece>
1943
+
1944
+ vtkfile_conns_vol.write('</UnstructuredGrid>'+'\n')
1945
+ #</UnstructuredGrid>
1946
+
1947
+ vtkfile_conns_vol.write('</VTKFile>'+'\n')
1948
+ #</VTKFile>
1949
+
1950
+ vtkfile_conns_vol.close()
1951
+
1952
+
1953
+ def BezierExtraction(NURBS_degree,nctrlpt_per_beam,nbeam_total):
1954
+ knotVec_per_beam = np.concatenate((np.zeros(NURBS_degree),(np.linspace(0,1,int((nctrlpt_per_beam-1)/NURBS_degree+1))),np.ones(NURBS_degree)))
1955
+ knotVec = np.tile(knotVec_per_beam, (nbeam_total,1))
1956
+ return knotVec
1957
+
1958
+
1959
+ def BezierBeamFile(geoName,NURBS_degree,nctrlpt_per_beam,\
1960
+ nconnector_t_per_beam,npatch,knotVec):
1961
+ ############################# Abaqus txt File #################################
1962
+ txtfile = open (Path('meshes/' + geoName + '/' + geoName + 'IGA.txt'),'w')
1963
+ txtfile.write('# Dimension of beam elements \n')
1964
+ txtfile.write('1 \n')
1965
+ txtfile.write('# Order of basis function \n')
1966
+ txtfile.write('{:d} \n'.format(NURBS_degree))
1967
+ txtfile.write('# Number of control points per patch \n')
1968
+ txtfile.write('{:d} \n'.format(nctrlpt_per_beam))
1969
+ txtfile.write('# Number of elements per patch \n')
1970
+ txtfile.write('{:d} \n'.format(nconnector_t_per_beam-1))
1971
+ txtfile.write('# Number of Patches \n')
1972
+ txtfile.write('{:d} \n'.format(npatch))
1973
+ # Loop over patches
1974
+ for ipatch in range(0,npatch):
1975
+ txtfile.write('{:s} \n'.format('PATCH-'+str(ipatch+1)))
1976
+ txtfile.write('Size of knot vectors \n')
1977
+ txtfile.write('{:d} \n'.format(knotVec.shape[1]))
1978
+ txtfile.write('knot vectors \n')
1979
+ for j in range(0,knotVec.shape[1]):
1980
+ txtfile.write('{:f} '.format(knotVec[ipatch,j]))
1981
+ txtfile.write('\n')
1982
+ txtfile.close()
1983
+
1984
+
1985
+ def ModelInfo(nbeam_per_grain,fiberlength,NURBS_degree,nctrlpt_per_beam,\
1986
+ props_beam,props_connector_t_bot,props_connector_t_reg,\
1987
+ props_connector_t_top,props_connector_l,iprops_beam,\
1988
+ beam_connectivity,connector_t_bot_connectivity,\
1989
+ connector_t_reg_connectivity,connector_t_top_connectivity,\
1990
+ connector_l_connectivity,MeshData):
1991
+
1992
+ beam_length = fiberlength/nbeam_per_grain
1993
+ nelem_beam = beam_connectivity.shape[0]
1994
+ nelem_connector_t_bot = connector_t_bot_connectivity.shape[0]
1995
+ nelem_connector_t_reg = connector_t_reg_connectivity.shape[0]
1996
+ nelem_connector_t_top = connector_t_top_connectivity.shape[0]
1997
+ nelem_connector_l = connector_l_connectivity.shape[0]
1998
+
1999
+ density_beam = props_beam[0]
2000
+ density_connector_t_bot = props_connector_t_bot[0]
2001
+ density_connector_t_top = props_connector_t_top[0]
2002
+ density_connector_t_reg = props_connector_t_reg[0]
2003
+ density_connector_l = props_connector_l[0]
2004
+ mass = 0
2005
+
2006
+ # mass of beams
2007
+ # for i in range(0,nelem_beam):
2008
+ mass += nelem_beam*density_beam*props_beam[3]*props_beam[4]*beam_length
2009
+ # mass of bot transverse connector
2010
+ for i in range(0,nelem_connector_t_bot):
2011
+ mass += density_connector_t_bot*props_connector_t_bot[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
2012
+ # mass of reg transverse connector
2013
+ for i in range(nelem_connector_t_bot,nelem_connector_t_bot+nelem_connector_t_reg):
2014
+ mass += density_connector_t_reg*props_connector_t_reg[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
2015
+ # mass of top transverse connector
2016
+ for i in range(nelem_connector_t_bot+nelem_connector_t_reg,nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top):
2017
+ mass += density_connector_t_top*props_connector_t_top[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
2018
+ # mass of longitudinal connector
2019
+ for i in range(nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,MeshData.shape[0]):
2020
+ mass += density_connector_l*props_connector_l[3]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
2021
+ return mass