Repository navigation
Expand file tree
/
Copy pathutils.py
More file actions
329 lines (230 loc) · 9.01 KB
/
Copy pathutils.py
File metadata and controls
329 lines (230 loc) · 9.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
from .core.globals import __GLOBALS__
from .core import const as __CONSTANTS__
import bpy, bmesh, re
# general
def get_number_safe(data):
t= type(data)
try:
return data if t is int else int(data)
except:
pass
return -1
# view 3d
def view3d_get(context):
area= context.area
if area.type == 'VIEW_3D':
area.tag_redraw()
rv3d = area.spaces[0].region_3d
for region in area.regions:
if region.type == 'WINDOW':
return region, rv3d
return None, None
def view3d_redraw_all(context):
for area in context.window.screen.areas:
if area.type == 'VIEW_3D':
area.tag_redraw()
# object
def get_active_object_bmesh(context):
if context.object.mode == 'EDIT':
bm= bmesh.from_edit_mesh(context.active_object.data)
else:
bm = bmesh.new()
bm.from_mesh(context.active_object.data)
return bm
def get_object_bmesh(obj):
bm = bmesh.new()
bm.from_mesh(obj.data)
return bm
def is_object_mesh(obj):
return obj and obj.type == 'MESH'
def is_object_jbeam(obj):
return __CONSTANTS__.ATTR_JBEAM_NODES in obj.data.attributes and __CONSTANTS__.ATTR_JBEAM_BEAMS in obj.data.attributes
# jbeam info
class jbeam_info():
select_history_mode= 'NONE'
select_history= []
select_history_len= 0
select_history_single= True
select_mode= 'NONE'
select_verts= []
select_verts_len= 0
select_verts_single= True
select_edges= []
select_edges_len= 0
select_edges_single= True
#select_faces= []
#select_faces_len= 0
#select_faces_single= True
select_len= 0
select_single= True
layer_nodes= None
layer_beams= None
def create_jbeam_info(bmesh_):
info= jbeam_info()
info.layer_nodes= bmesh_.verts.layers.string.get(__CONSTANTS__.ATTR_JBEAM_NODES)
info.layer_beams= bmesh_.edges.layers.string.get(__CONSTANTS__.ATTR_JBEAM_BEAMS)
# normal selection
select_mode= set()
if 'VERT' in bmesh_.select_mode: select_mode.add('NODE')
info.select_verts= [se for se in bmesh_.verts if se.select]
info.select_verts_len= len(info.select_verts)
info.select_verts_single= info.select_len < 2
if 'EDGE' in bmesh_.select_mode: select_mode.add('BEAM')
info.select_edges= [se for se in bmesh_.edges if se.select]
info.select_edges_len= len(info.select_edges)
info.select_edges_single= info.select_len < 2
#if 'FACE' in bmesh_.select_mode: select_mode.add('FACE')
#info.select_faces= [se for se in bmesh_.faces if se.select]
#info.select_faces_len= len(info.select_faces)
#info.select_faces_single= info.select_len < 2
info.select_len= info.select_verts_len + info.select_edges_len
info.select_single= info.select_len < 2
info.select_mode= select_mode
# selection history
history= bmesh_.select_history
_vert= isinstance(history.active, bmesh.types.BMVert)
_edge= not _vert and isinstance(history.active, bmesh.types.BMEdge)
info.select_history= history
info.select_history_len= len(history)
info.select_history_single= info.select_history_len < 2
info.select_history_mode= 'NODE' if _vert else 'BEAM' if _edge else 'NONE'
info.use_select= (not _vert and not _edge) or (info.select_history_len < (info.select_verts_len if _vert else info.select_edges_len))
info.select= history if not info.use_select else ( info.select_verts if 'VERT' in bmesh_.select_mode else info.select_edges )
return info
def get_vert_selection(jinfo):
if jinfo.use_select or jinfo.select_history_mode == 'BEAM':
if jinfo.select_verts_len > 0:
select= set(jinfo.select_verts)
return select, None
elif jinfo.select_history_len > 0 and jinfo.select_history_mode == 'NODE':
active= jinfo.select_history.active
select= set([v for v in jinfo.select_history if v.index != active.index])
return select, active
return None, None
def get_edge_selection(jinfo):
if jinfo.use_select or jinfo.select_history_mode == 'NODE':
if jinfo.select_edges_len > 0:
select= set(jinfo.select_edges)
return select, None
elif jinfo.select_history_len > 0:
if jinfo.select_history_mode == 'BEAM':
active= jinfo.select_history.active
select= set([e for e in jinfo.select_history if e.index != active.index])
return select, active
return None, None
def get_all_vert_raw(jinfo):
select= jinfo.use_select
if select and jinfo.select_verts_len > 0 or jinfo.select_history_mode == 'NODE':
return jinfo.select_verts if select else jinfo.select_history
elif select and jinfo.select_edges_len > 0 or jinfo.select_history_mode == 'BEAM':
selection= jinfo.select_edges if select else jinfo.select_history
verts= set([v for e in selection for v in e.verts])
return list(verts)
return None
# string utils
def get_node_data_from_attribute(string: str):
""" Split the attribute into data """
return string.split('|')
def get_node_name_full(attribute: bytes):
""" Get full name string from attribute """
return str(attribute, 'utf-8').split('|')[0]
def compose_node_data_raw(data:list) -> str:
""" Create the node data to be stored in attributes """
nstr= type(data[2]) != str
data[2]= "" if (nstr and data[2] < 0) else str(data[2]) if nstr else data[2]
data[0]= data[1] + data[2] + data[3]
return '|'.join(data)
def compose_node_data(prefix:str= "n", number:int|None=-1, side:str|None="") -> str:
""" Create the node data to be stored in attributes """
name= prefix + n + side
n = "" if (type(number) == int and number < 0) else number
return name + "|" + prefix + "|" + n + "|" + side
# sortening utils
def get_name_features(name: str):
try:
match= re.fullmatch(r"(([A-z_]+)*(?=[0-9])([0-9]+)([A-z]|))", name)
return match[2], match[3], match[4]
except: pass
return name, "", ""
def get_sortable_name(name:str, ascending:bool, side_over_name:bool, left_first:bool):
pre, num, side= get_name_features(name)
num= int(num) if num != "" else 100000
side_priority= side.startswith("l") and left_first
side_factor= ("0" if side_priority else "1")*len(side)
data= []
if side_over_name:
data.append(side_factor)
data.append(pre)
num= str(num if ascending else 100000-num)
if len(num) < 6: num= ("0"*(6-len(num))) + num
data.append(num)
if not side_over_name:
data.append(side_factor)
return "|".join(data)
def sort_node(element, ascending=True, side_over_name=True, left_first=True):
name= re.search(r"[\"' ]+([A-z0-9_-]+)[\"' ],", element)[1]
sortable_str= get_sortable_name(name, ascending, side_over_name, left_first)
print(f"sorting node: '{name}', '{sortable_str}'")
return sortable_str
def sort_beam(element, ascending=True, side_over_name=True, left_first=True, dest_over_src=False):
name_a= re.search(r"[\"' ]+([A-z0-9_-]+)[\"' ],", element)[1]
data_a= get_sortable_name(name_a, ascending, side_over_name, left_first)
name_b= re.search(r",[\"' ]+([A-z0-9_-]+)[\"' ]", element)[1]
data_b= get_sortable_name(name_b, ascending, side_over_name, left_first)
sortable_str= f"{data_b}|{data_a}" if dest_over_src else f"{data_a}|{data_b}"
print(f"sorting beam: '{name_a}' to '{name_b}', '{sortable_str}'")
return sortable_str
# set prefix for current selection
def set_selection_prefix(jinfo, value):
view3d_redraw_all(bpy.context)
if re.match(r"^[A-z_]+$", value)[0]:
verts= get_all_vert_raw(jinfo)
if verts:
for v in verts:
attrdata= get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))
attrdata[1]= value
v[jinfo.layer_nodes]= bytes(compose_node_data_raw(attrdata), 'utf-8')
# get most used prefix in current selection
def get_selection_prefix(jinfo):
verts= get_all_vert_raw(jinfo)
if verts:
prefix_list= [get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))[1] for v in verts]
prefix_set = set(prefix_list)
prefix_count= dict((e, prefix_list.count(e)) for e in prefix_set)
key= max(prefix_count, key=lambda k: prefix_count[k])
return key
return None
# set selection numerals
def set_selection_numerals(jinfo, values):
view3d_redraw_all(bpy.context)
verts= get_all_vert_raw(jinfo)
if verts:
for i in range(len(verts)):
v= verts[i]
attrdata= get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))
attrdata[2]= values[i]
v[jinfo.layer_nodes]= bytes(compose_node_data_raw(attrdata), 'utf-8')
# get selection numerals
def get_selection_numerals(jinfo):
verts= get_all_vert_raw(jinfo)
values= []
if verts:
values= [get_number_safe(get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))[2]) for v in verts]
return values
# set selection side
def set_selection_side(jinfo, values):
view3d_redraw_all(bpy.context)
verts= get_all_vert_raw(jinfo)
if verts:
for i in range(len(verts)):
v= verts[i]
attrdata= get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))
attrdata[3]= values[i]
v[jinfo.layer_nodes]= bytes(compose_node_data_raw(attrdata), 'utf-8')
# get selection side
def get_selection_side(jinfo):
verts= get_all_vert_raw(jinfo)
values= []
if verts:
values= [get_node_data_from_attribute(str(v[jinfo.layer_nodes], 'utf-8'))[3] for v in verts]
return values