midas-civil 1.4.1__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.
Files changed (40) hide show
  1. midas_civil/_BoundaryChangeAssignment.py +278 -0
  2. midas_civil/__init__.py +51 -0
  3. midas_civil/_analysiscontrol.py +585 -0
  4. midas_civil/_boundary.py +888 -0
  5. midas_civil/_construction.py +1004 -0
  6. midas_civil/_element.py +1346 -0
  7. midas_civil/_group.py +337 -0
  8. midas_civil/_load.py +967 -0
  9. midas_civil/_loadcomb.py +159 -0
  10. midas_civil/_mapi.py +249 -0
  11. midas_civil/_material.py +1692 -0
  12. midas_civil/_model.py +522 -0
  13. midas_civil/_movingload.py +1479 -0
  14. midas_civil/_node.py +532 -0
  15. midas_civil/_result_table.py +929 -0
  16. midas_civil/_result_test.py +5455 -0
  17. midas_civil/_section/_TapdbSecSS.py +175 -0
  18. midas_civil/_section/__init__.py +413 -0
  19. midas_civil/_section/_compositeSS.py +283 -0
  20. midas_civil/_section/_dbSecSS.py +164 -0
  21. midas_civil/_section/_offsetSS.py +53 -0
  22. midas_civil/_section/_pscSS copy.py +455 -0
  23. midas_civil/_section/_pscSS.py +822 -0
  24. midas_civil/_section/_tapPSC12CellSS.py +565 -0
  25. midas_civil/_section/_unSupp.py +58 -0
  26. midas_civil/_settlement.py +161 -0
  27. midas_civil/_temperature.py +677 -0
  28. midas_civil/_tendon.py +1016 -0
  29. midas_civil/_thickness.py +147 -0
  30. midas_civil/_utils.py +529 -0
  31. midas_civil/_utilsFunc/__init__.py +0 -0
  32. midas_civil/_utilsFunc/_line2plate.py +636 -0
  33. midas_civil/_view.py +891 -0
  34. midas_civil/_view_trial.py +430 -0
  35. midas_civil/_visualise.py +347 -0
  36. midas_civil-1.4.1.dist-info/METADATA +74 -0
  37. midas_civil-1.4.1.dist-info/RECORD +40 -0
  38. midas_civil-1.4.1.dist-info/WHEEL +5 -0
  39. midas_civil-1.4.1.dist-info/licenses/LICENSE +21 -0
  40. midas_civil-1.4.1.dist-info/top_level.txt +1 -0
midas_civil/_node.py ADDED
@@ -0,0 +1,532 @@
1
+ from ._mapi import MidasAPI
2
+ from ._utils import zz_add_to_dict,_convItem2List , sFlatten
3
+ from math import hypot
4
+ from ._group import _add_node_2_stGroup
5
+ from typing import Literal
6
+ import numpy as np
7
+ from ._group import Group
8
+
9
+ def dist_tol(a,b):
10
+ return hypot((a.X-b.X),(a.Y-b.Y),(a.Z-b.Z)) < 0.00001 #TOLERANCE BUILT IN (UNIT INDEP)
11
+
12
+ def cell(point,size=1): #SIZE OF GRID - string format
13
+ # return str(f"{int(point.X//size)},{int(point.Y//size)},{int(point.Z//size)}")
14
+ return str(f"{int(point.X)},{int(point.Y)},{int(point.Z)}")
15
+
16
+ # def _cellGrid():
17
+ # [float(x.strip()) for x in list(Node.Grid.keys())split(",")]
18
+ # -------- FUNCTIONS ARE DEFINED BELOW TO RECOGNISE NODE CLASS ----------------
19
+
20
+
21
+ class _hNode:
22
+ ID,X,Y,Z,AXIS = 0,0,0,0,0
23
+
24
+ #5 Class to create nodes
25
+ class Node:
26
+ """X ordinate, Y ordinate, Z ordinate, Node ID (optional). \nSample: Node(1,0,5)"""
27
+ nodes:list[_hNode] = [] # Node object stores in a list
28
+ ids:list[int] = [] # Node IDs used for auto increment of ID and replacement of nodes
29
+ Grid ={} # Node object in cube grid
30
+ __nodeDic__ = {} # Stores node object corresponding to ID (faster get with nodebyID)
31
+ def __init__(self,x:float,y:float,z:float,id:int=None,group:str='',merge:bool=True):
32
+ ''' Create Node object
33
+
34
+ Parameters:
35
+ x: X - ordinate of node
36
+ y: Y - ordinate of node
37
+ z: Z - ordinate of node
38
+ id: Node ID (default 0 for auto-increment)
39
+ mat: Material property number (default 1)
40
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
41
+ merge: If enabled, checks for existing nodes and return their IDs. No additional/duplicate node will be created.
42
+
43
+ Examples:
44
+ ```python
45
+ Node(0,0,0, id =1 , group = 'Support', merge=1)
46
+ ```
47
+
48
+ '''
49
+
50
+ if id == None: id =0
51
+ #----------------- ORIGINAL -----------------------
52
+
53
+ if Node.ids == []:
54
+ node_count = 1
55
+ else:
56
+ node_count = max(Node.ids)+1
57
+
58
+
59
+ self.X = round(x,6)
60
+ self.Y = round(y,6)
61
+ self.Z = round(z,6)
62
+
63
+ if id == 0 : self.ID = node_count
64
+ if id != 0 : self.ID = id
65
+
66
+
67
+ #REPLACE - No merge check
68
+ if id in Node.ids:
69
+
70
+ index=Node.ids.index(id)
71
+ n_orig = Node.nodes[index]
72
+ loc_orig = str(cell(n_orig))
73
+ Node.Grid[loc_orig].remove(n_orig)
74
+
75
+ loc_new = str(cell(self))
76
+
77
+ zz_add_to_dict(Node.Grid,loc_new,self)
78
+ Node.nodes[index]=self
79
+ Node.__nodeDic__[str(id)] = self
80
+
81
+
82
+ #CREATE NEW - Merge Check based on input
83
+ else:
84
+ self.AXIS = [[0,0,0],[0,0,0],[0,0,0]]
85
+ cell_loc = str(cell(self))
86
+
87
+ if cell_loc in Node.Grid:
88
+
89
+ if merge :
90
+ chk=0 #OPTIONAL
91
+ for node in Node.Grid[cell_loc]:
92
+ if dist_tol(self,node):
93
+
94
+ chk=1
95
+ self.ID=node.ID
96
+ self.AXIS = node.AXIS
97
+ if chk==0:
98
+
99
+ self.AXIS = [[0,0,0],[0,0,0],[0,0,0]]
100
+ Node.nodes.append(self)
101
+ Node.ids.append(self.ID)
102
+ Node.Grid[cell_loc].append(self)
103
+
104
+
105
+ else:
106
+
107
+ Node.nodes.append(self)
108
+ Node.ids.append(self.ID)
109
+ Node.Grid[cell_loc].append(self)
110
+ else:
111
+
112
+ Node.Grid[cell_loc]=[]
113
+ Node.nodes.append(self)
114
+ Node.ids.append(self.ID)
115
+ Node.Grid[cell_loc].append(self)
116
+ Node.__nodeDic__[str(self.ID)] = self
117
+
118
+ if group !="":
119
+ _add_node_2_stGroup(self.ID,group)
120
+
121
+ @property
122
+ def LOC(self):
123
+ ''' Return X,Y,Z as a tuple'''
124
+ return (self.X,self.Y,self.Z)
125
+
126
+ def __str__(self):
127
+ return f"NODE ID : {self.ID} | X:{self.X} , Y:{self.Y} , Z:{self.Z} \n{self.__dict__}"
128
+
129
+ @classmethod
130
+ def json(cls):
131
+ json = {"Assign":{}}
132
+ for i in cls.nodes:
133
+ json["Assign"][i.ID]={"X":i.X,"Y":i.Y,"Z":i.Z}
134
+ return json
135
+
136
+ @staticmethod
137
+ def create():
138
+ MidasAPI("PUT","/db/NODE",Node.json())
139
+
140
+ @staticmethod
141
+ def get():
142
+ return MidasAPI("GET","/db/NODE")
143
+
144
+ @staticmethod
145
+ def sync():
146
+ Node.clear()
147
+ a = Node.get()
148
+ if a != {'message': ''}:
149
+ if list(a['NODE'].keys()) != []:
150
+ for j in a['NODE'].keys():
151
+ Node(round(a['NODE'][j]['X'],6), round(a['NODE'][j]['Y'],6), round(a['NODE'][j]['Z'],6), id=int(j), group='', merge=False)
152
+
153
+
154
+ @staticmethod
155
+ def delete():
156
+ MidasAPI("DELETE","/db/NODE/")
157
+ Node.clear()
158
+
159
+ @staticmethod
160
+ def clear():
161
+ Node.nodes=[]
162
+ Node.ids=[]
163
+ Node.Grid={}
164
+ Node.__nodeDic__ = {}
165
+
166
+ @staticmethod
167
+ def SE(s_loc:list,e_loc:list,n:int=1,id:int=None,group:str='',merge:bool=True):
168
+ if isinstance(s_loc,Node):
169
+ s_loc = (s_loc.X,s_loc.Y,s_loc.Z)
170
+ if isinstance(e_loc,Node):
171
+ e_loc = (e_loc.X,e_loc.Y,e_loc.Z)
172
+
173
+ beam_nodes =[]
174
+ i_loc = np.linspace(s_loc,e_loc,n+1)
175
+ for i in range(n+1):
176
+ beam_nodes.append(Node(i_loc[i][0].item(),i_loc[i][1].item(),i_loc[i][2].item(),id,group,merge))
177
+
178
+ return beam_nodes
179
+
180
+ @staticmethod
181
+ def SDL(s_loc:list,dir:list,l:float,n:int=1,id:int=None,group:str='',merge:bool=True):
182
+ if isinstance(s_loc,Node):
183
+ s_loc = (s_loc.X,s_loc.Y,s_loc.Z)
184
+
185
+ beam_nodes =[]
186
+ s_locc = np.array(s_loc)
187
+ unit_vec = np.array(dir)/np.linalg.norm(dir)
188
+
189
+ for i in range(n+1):
190
+ locc = s_locc+i*l*unit_vec/n
191
+ beam_nodes.append(Node(locc[0].item(),locc[1].item(),locc[2].item(),id,group,merge))
192
+
193
+ return beam_nodes
194
+
195
+
196
+
197
+
198
+
199
+
200
+ # ---- GET NODE OBJECT FROM ID ----------
201
+
202
+ # def nodeByID(nodeID:int) -> Node:
203
+ # ''' Return Node object with the input ID '''
204
+ # for node in Node.nodes:
205
+ # if node.ID == nodeID:
206
+ # return node
207
+
208
+ # print(f'There is no node with ID {nodeID}')
209
+ # return None
210
+
211
+ def nodesInGroup(groupName:str,unique:bool=True,reverse:bool=False,output:Literal['ID','NODE']='ID') -> list[Node]:
212
+ ''' Returns Node ID list or Node objects in a Structure Group or list of Structure groups
213
+ eg. nodesInGroup('SG_A')
214
+ nodesInGroup(['SG_1','SG_2','SG_3'])
215
+ groupName : 'SG_A' or ['SG_1' , 'SG_2' , 'SG_2']
216
+ unique : True -> Only unique ID is returned.
217
+ In case of multiple groups, we may require only uniques ids
218
+ reverse : True -> Reverses the returned list
219
+ '''
220
+ groupNames = _convItem2List(groupName)
221
+ nlist = []
222
+ for gName in groupNames:
223
+ chk=1
224
+ rev = reverse
225
+ if gName[0] == '!':
226
+ gName = gName[1:]
227
+ rev = not rev
228
+ for i in Group.Structure.Groups:
229
+ if i.NAME == gName:
230
+ chk=0
231
+ nIDlist = i.NLIST
232
+ if rev: nIDlist = list(reversed(nIDlist))
233
+ nlist.append(nIDlist)
234
+ if chk:
235
+ print(f'⚠️ "{gName}" - Structure group not found !')
236
+ if unique:
237
+ finalNlist = list(dict.fromkeys(sFlatten(nlist)))
238
+ else:
239
+ finalNlist = sFlatten(nlist)
240
+
241
+ if output == 'NODE':
242
+ finoutput = []
243
+ for nod in finalNlist:
244
+ finoutput.append(nodeByID(nod))
245
+ finalNlist:Node = finoutput
246
+
247
+ return finalNlist
248
+
249
+ def nodeByID(nodeID:int) -> Node:
250
+ ''' Return Node object with the input ID '''
251
+ try:
252
+ return (Node.__nodeDic__[str(nodeID)])
253
+ except:
254
+ print(f'There is no node with ID {nodeID}')
255
+ return None
256
+
257
+ def closestNode(point_location:list) -> Node:
258
+ ''' Enter location to find nearest node
259
+ list [x,y,z] => point location => Nearest node
260
+ node object => nearest remaining node
261
+ int => node with ID provided => nearest remaining node
262
+ '''
263
+ gridStr = list(Node.Grid.keys())
264
+ gridInt = []
265
+ for key in gridStr:
266
+ gridInt.append([int(x) for x in key.split(",")])
267
+
268
+ bNode = False
269
+ bNodeID = 0
270
+ if isinstance(point_location,int):
271
+ bNode = True
272
+ bNodeID = point_location
273
+ nodeP = nodeByID(point_location)
274
+ point_location = (nodeP.X,nodeP.Y,nodeP.Z)
275
+ elif isinstance(point_location,Node):
276
+ bNode = True
277
+ bNodeID = point_location.ID
278
+ point_location = (point_location.X,point_location.Y,point_location.Z)
279
+ pGridInt = [int(point_location[0]),int(point_location[1]),int(point_location[2])]
280
+ pGridStr = f"{int(point_location[0])},{int(point_location[1])},{int(point_location[2])}"
281
+
282
+ min_edge_dist = round(min(point_location[0]-pGridInt[0],point_location[1]-pGridInt[1],point_location[2]-pGridInt[2]),3)
283
+ max_edge_dist = round(max(point_location[0]-pGridInt[0],point_location[1]-pGridInt[1],point_location[2]-pGridInt[2]),3)
284
+
285
+ if min_edge_dist > 0.5 : min_edge_dist = round(1-min_edge_dist,3)
286
+ if max_edge_dist > 0.5 : max_edge_dist = round(1-max_edge_dist,3)
287
+
288
+ min_edge_dist = min(min_edge_dist,max_edge_dist)
289
+
290
+ min_dist = 10000000000 #Large value for initial value
291
+ min_node = 0
292
+ checked_GridInt = []
293
+
294
+ if bNode and len(Node.Grid[pGridStr]) == 1:
295
+ gridDist = []
296
+ for gInt in gridInt:
297
+ gridDist.append(abs(gInt[0]-pGridInt[0])+abs(gInt[1]-pGridInt[1])+abs(gInt[2]-pGridInt[2]))
298
+ gridDistSort = sorted(gridDist)
299
+
300
+ nearestGridIdx = gridDist.index(gridDistSort[1])
301
+ nearestGridInt = gridInt[nearestGridIdx]
302
+ nearestGridStr = gridStr[nearestGridIdx]
303
+ else:
304
+ if pGridInt in gridInt :
305
+ nearestGridInt = pGridInt
306
+ nearestGridStr = pGridStr
307
+ else :
308
+ gridDist = []
309
+ for gInt in gridInt:
310
+ gridDist.append(abs(gInt[0]-pGridInt[0])+abs(gInt[1]-pGridInt[1])+abs(gInt[2]-pGridInt[2]))
311
+
312
+ nearestGridIdx = gridDist.index(min(gridDist))
313
+ nearestGridInt = gridInt[nearestGridIdx]
314
+ nearestGridStr = gridStr[nearestGridIdx]
315
+
316
+ for nd in Node.Grid[nearestGridStr]:
317
+ dist = hypot(nd.X-point_location[0],nd.Y-point_location[1],nd.Z-point_location[2])
318
+ if dist < min_dist and nd.ID !=bNodeID:
319
+ min_dist = dist
320
+ min_node = nd
321
+ checked_GridInt.append(nearestGridInt)
322
+ if min_dist < min_edge_dist :
323
+ return min_node
324
+
325
+ else:
326
+ # COMBINATION POSSIBLE FOR CELLS
327
+ minX = int(point_location[0]-min_dist)
328
+ maxX = int(point_location[0]+min_dist)
329
+ minY = int(point_location[1]-min_dist)
330
+ maxY = int(point_location[1]+min_dist)
331
+ minZ = int(point_location[2]-min_dist)
332
+ maxZ = int(point_location[2]+min_dist)
333
+ possible = maxX+maxY+maxZ-minX-minY-minZ
334
+ if possible == 0:
335
+ return min_node
336
+
337
+ for i in np.arange(minX,maxX+1,1):
338
+ for j in np.arange(minY,maxY+1,1):
339
+ for k in np.arange(minZ,maxZ+1,1):
340
+ cgridStr = f"{i},{j},{k}"
341
+ cgridInt = [i,j,k]
342
+
343
+ if cgridInt in checked_GridInt:
344
+ continue
345
+ else:
346
+ if cgridInt in gridInt:
347
+ for nd in Node.Grid[cgridStr]:
348
+ dist = hypot(nd.X-point_location[0],nd.Y-point_location[1],nd.Z-point_location[2])
349
+ if dist < min_dist and nd.ID !=bNodeID:
350
+ min_dist = dist
351
+ min_node = nd
352
+ checked_GridInt.append(cgridInt)
353
+ return min_node
354
+
355
+ def _ifNodeExist_(x,y,z) -> tuple:
356
+ cell_loc = str(f"{int(x)},{int(y)},{int(z)}")
357
+ if cell_loc in Node.Grid:
358
+ for node in Node.Grid[cell_loc]:
359
+ if hypot((x-node.X),(y-node.Y),(z-node.Z)) < 0.00001 :
360
+ return True,node.ID
361
+ return False,0
362
+
363
+
364
+ def nodesInRadius(point_location:list , radius:float=0, output :Literal['ID','NODE'] = 'ID',includeSelf = False)-> list:
365
+ gridStr = list(Node.Grid.keys())
366
+
367
+ bNode = False
368
+ id2Remove = 0
369
+ if isinstance(point_location,int):
370
+ bNode = True
371
+ id2Remove = point_location
372
+ nodeP = nodeByID(point_location)
373
+ point_location = (nodeP.X,nodeP.Y,nodeP.Z)
374
+
375
+ elif isinstance(point_location,Node):
376
+ bNode = True
377
+ id2Remove = point_location.ID
378
+ point_location = (point_location.X,point_location.Y,point_location.Z)
379
+
380
+ if not includeSelf and not bNode:
381
+ bNode,id2Remove = _ifNodeExist_(point_location[0],point_location[1],point_location[2])
382
+
383
+ ifRemove = bNode and not includeSelf
384
+
385
+ checked_GridStr = []
386
+ close_nodes:list[int] = []
387
+ close_nodesID:list[Node] = []
388
+
389
+
390
+ minX = int(point_location[0]-radius)
391
+ maxX = int(point_location[0]+radius)
392
+ minY = int(point_location[1]-radius)
393
+ maxY = int(point_location[1]+radius)
394
+ minZ = int(point_location[2]-radius)
395
+ maxZ = int(point_location[2]+radius)
396
+
397
+ for i in np.arange(minX,maxX+1,1):
398
+ for j in np.arange(minY,maxY+1,1):
399
+ for k in np.arange(minZ,maxZ+1,1):
400
+ cgridStr = f"{i},{j},{k}"
401
+ if cgridStr in checked_GridStr:
402
+ # print("Grid already checked")
403
+ continue
404
+ else:
405
+ if cgridStr in gridStr:
406
+ for nd in Node.Grid[cgridStr]:
407
+ dist = hypot(nd.X-point_location[0],nd.Y-point_location[1],nd.Z-point_location[2])
408
+ if dist <= radius+0.0001 :
409
+ close_nodes.append(nd)
410
+ close_nodesID.append(nd.ID)
411
+ checked_GridStr.append(cgridStr)
412
+
413
+ if output == 'Node':
414
+ if ifRemove:
415
+ close_nodes.remove(nodeByID(id2Remove))
416
+ return close_nodes
417
+ if ifRemove:
418
+ close_nodesID.remove(id2Remove)
419
+ return close_nodesID
420
+
421
+
422
+
423
+ class NodeLocalAxis:
424
+ skew = []
425
+ ids = []
426
+
427
+ def __init__(self,nodeID:int,type:Literal['X' , 'Y' , 'Z' , 'XYZ','Vector'],angle:list):
428
+ '''
429
+ nodeID(int) : ID of the node
430
+ axis (str) : Axis of rotation, 'X' , 'Y' , 'Z' , 'XYZ' or 'Vector'
431
+ angle (float) : Angle of rotation if axis = 'X' , 'Y' or 'Z' ;
432
+ angle (list : float) = [30,0,0] if type = 'XYZ'
433
+ angle (list : vector) -> node.AXIS = [[1,0,0],[0,1,0]] if type = 'Vector'
434
+ '''
435
+
436
+ self.ID = nodeID
437
+
438
+ if nodeID in NodeLocalAxis.ids:
439
+ index = NodeLocalAxis.ids.index(nodeID)
440
+ intial_angle = NodeLocalAxis.skew[index].ANGLE
441
+ if intial_angle == [[0,0,0],[0,0,0],[0,0,0]]:
442
+ intial_angle = [[1,0,0],[0,1,0],[0,0,1]]
443
+
444
+ if type == 'Vector':
445
+ self.TYPE = 'VEC'
446
+ self.VEC = angle
447
+ elif type == 'X':
448
+ self.TYPE = 'ANGLE'
449
+ self.ANGLE = [angle,intial_angle[1],intial_angle[2]]
450
+ elif type == 'Y':
451
+ self.TYPE = 'ANGLE'
452
+ self.ANGLE = [intial_angle[0],angle,intial_angle[2]]
453
+ elif type == 'Z':
454
+ self.TYPE = 'ANGLE'
455
+ self.ANGLE = [intial_angle[0],intial_angle[1],angle]
456
+ elif type == 'XYZ':
457
+ self.TYPE = 'ANGLE'
458
+ self.ANGLE = angle
459
+ NodeLocalAxis.skew[index] = self
460
+ else:
461
+ if type == 'Vector':
462
+ self.TYPE = 'VEC'
463
+ self.VEC = angle
464
+ self.ANGLE = [0,0,0]
465
+ elif type == 'X':
466
+ self.TYPE = 'ANGLE'
467
+ self.ANGLE = [angle,0,0]
468
+ elif type == 'Y':
469
+ self.TYPE = 'ANGLE'
470
+ self.ANGLE = [0,angle,0]
471
+ elif type == 'Z':
472
+ self.TYPE = 'ANGLE'
473
+ self.ANGLE = [0,0,angle]
474
+ elif type == 'XYZ':
475
+ self.TYPE = 'ANGLE'
476
+ self.ANGLE = angle
477
+
478
+ NodeLocalAxis.skew.append(self)
479
+ NodeLocalAxis.ids.append(self.ID)
480
+
481
+ @classmethod
482
+ def json(cls):
483
+ json = {"Assign":{}}
484
+ for i in cls.skew:
485
+ if i.TYPE == 'ANGLE':
486
+ json["Assign"][i.ID]={
487
+ "iMETHOD": 1,
488
+ "ANGLE_X": i.ANGLE[0],
489
+ "ANGLE_Y": i.ANGLE[1],
490
+ "ANGLE_Z": i.ANGLE[2]
491
+ }
492
+ elif i.TYPE == 'VEC':
493
+ json["Assign"][i.ID]={
494
+ "iMETHOD": 3,
495
+ "V1X": i.VEC[0][0],
496
+ "V1Y": i.VEC[0][1],
497
+ "V1Z": i.VEC[0][2],
498
+ "V2X": i.VEC[1][0],
499
+ "V2Y": i.VEC[1][1],
500
+ "V2Z": i.VEC[1][2]
501
+ }
502
+ return json
503
+
504
+ @staticmethod
505
+ def create():
506
+ MidasAPI("PUT","/db/SKEW",NodeLocalAxis.json())
507
+
508
+ @staticmethod
509
+ def delete():
510
+ MidasAPI("DELETE","/db/SKEW/")
511
+ NodeLocalAxis.clear()
512
+
513
+ @staticmethod
514
+ def clear():
515
+ NodeLocalAxis.skew=[]
516
+ NodeLocalAxis.ids=[]
517
+
518
+ @staticmethod
519
+ def get():
520
+ return MidasAPI("GET","/db/SKEW")
521
+
522
+ # @staticmethod
523
+ # def sync():
524
+ # NodeLocalAxis.skew=[]
525
+ # NodeLocalAxis.ids=[]
526
+ # a = NodeLocalAxis.get()
527
+ # if a != {'message': ''}:
528
+ # if list(a['NODE'].keys()) != []:
529
+
530
+ # for j in a['NODE'].keys():
531
+
532
+ # Node(round(a['NODE'][j]['X'],6), round(a['NODE'][j]['Y'],6), round(a['NODE'][j]['Z'],6), id=int(j), group='', merge=0)