pyjallib 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.
pyjallib/max/layer.py ADDED
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Layer 모듈 - 3ds Max 레이어 관리 기능
6
+ 원본 MAXScript의 layer.ms를 Python으로 변환
7
+ """
8
+
9
+ from pymxs import runtime as rt
10
+ import pymxs
11
+
12
+ class Layer:
13
+ """
14
+ 레이어 관련 기능을 위한 클래스
15
+ MAXScript의 _Layer 구조체를 Python 클래스로 변환
16
+
17
+ pymxs 모듈을 통해 3ds Max의 레이어 관리 기능을 제어합니다.
18
+ """
19
+
20
+ def __init__(self):
21
+ """
22
+ 초기화 함수
23
+ """
24
+ pass
25
+
26
+ def reset_layer(self):
27
+ """
28
+ 모든 레이어를 초기화하고 기본 레이어로 객체 이동
29
+
30
+ Returns:
31
+ None
32
+ """
33
+ # 기본 레이어(0번 레이어) 가져오기
34
+ defaultLayer = rt.layerManager.getLayer(0)
35
+ layerNameArray = []
36
+ defaultLayer.current = True
37
+
38
+ # 레이어가 1개 이상 존재하면
39
+ if rt.LayerManager.count > 1:
40
+ # 모든 레이어 순회하며 객체들을 기본 레이어로 이동
41
+ for i in range(1, rt.layerManager.count):
42
+ ilayer = rt.layerManager.getLayer(i)
43
+ layerName = ilayer.name
44
+ layerNameArray.append(layerName)
45
+
46
+ layer = rt.ILayerManager.getLayerObject(i)
47
+ layerNodes = rt.refs.dependents(layer)
48
+
49
+ # 레이어의 모든 노드를 기본 레이어로 이동
50
+ for item in layerNodes:
51
+ if rt.isValidNode(item):
52
+ defaultLayer.addNode(item)
53
+
54
+ # 모든 레이어 삭제
55
+ for item in layerNameArray:
56
+ rt.LayerManager.deleteLayerByName(item)
57
+
58
+ def get_nodes_from_layer(self, inLayerNum):
59
+ """
60
+ 레이어 번호로 해당 레이어의 노드들을 가져옴
61
+
62
+ Args:
63
+ inLayerNum: 레이어 번호
64
+
65
+ Returns:
66
+ 레이어에 포함된 노드 배열 또는 빈 배열
67
+ """
68
+ layer = rt.ILayerManager.getLayerObject(inLayerNum)
69
+ if layer is not None:
70
+ layerNodes = rt.refs.dependents(layer)
71
+ return layerNodes
72
+ else:
73
+ return rt.Array()
74
+
75
+ def get_layer_number(self, inLayerName):
76
+ """
77
+ 레이어 이름으로 레이어 번호를 찾음
78
+
79
+ Args:
80
+ inLayerName: 레이어 이름
81
+
82
+ Returns:
83
+ 레이어 번호 또는 False (없는 경우)
84
+ """
85
+ # 모든 레이어를 순회하며 이름 비교
86
+ for i in range(rt.LayerManager.count):
87
+ layer = rt.layerManager.getLayer(i)
88
+ if layer.name == inLayerName:
89
+ return i
90
+
91
+ return False
92
+
93
+ def get_nodes_by_layername(self, inLayerName):
94
+ """
95
+ 레이어 이름으로 해당 레이어의 노드들을 가져옴
96
+
97
+ Args:
98
+ inLayerName: 레이어 이름
99
+
100
+ Returns:
101
+ 레이어에 포함된 노드 배열
102
+ """
103
+ return self.get_nodes_from_layer(self.get_layer_number(inLayerName))
104
+
105
+ def del_empty_layer(self, showLog=False):
106
+ """
107
+ 빈 레이어 삭제
108
+
109
+ Args:
110
+ showLog: 삭제 결과 메시지 표시 여부
111
+
112
+ Returns:
113
+ None
114
+ """
115
+ deleted_layer_count = 0
116
+ deflayer = rt.layermanager.getlayer(0)
117
+ deflayer.current = True
118
+
119
+ # 모든 레이어를 역순으로 순회 (삭제 시 인덱스 변경 문제 방지)
120
+ for i in range(rt.Layermanager.count-1, 0, -1):
121
+ layer = rt.layermanager.getLayer(i)
122
+ thisLayerName = layer.name
123
+ nodes = self.get_nodes_from_layer(i)
124
+
125
+ # 노드가 없는 레이어 삭제
126
+ if len(nodes) == 0:
127
+ rt.LayerManager.deleteLayerbyname(thisLayerName)
128
+ deleted_layer_count += 1
129
+
130
+ # 로그 표시 옵션이 활성화되어 있고 삭제된 레이어가 있는 경우
131
+ if showLog and deleted_layer_count != 0:
132
+ print(f"Number of layers removed = {deleted_layer_count}")
133
+
134
+ def create_layer_from_array(self, inArray, inLayerName):
135
+ """
136
+ 객체 배열로 새 레이어 생성
137
+
138
+ Args:
139
+ inArray: 레이어에 추가할 객체 배열
140
+ inLayerName: 생성할 레이어 이름
141
+
142
+ Returns:
143
+ 생성된 레이어
144
+ """
145
+ new_layer = None
146
+ layer_index = self.get_layer_number(inLayerName)
147
+
148
+ # 레이어가 없으면 새로 생성, 있으면 기존 레이어 사용
149
+ if layer_index is False:
150
+ new_layer = rt.LayerManager.newLayer()
151
+ new_layer.setName(inLayerName)
152
+ else:
153
+ new_layer = rt.layerManager.getLayer(layer_index)
154
+
155
+ # 모든 객체를 레이어에 추가
156
+ for item in inArray:
157
+ new_layer.addNode(item)
158
+
159
+ return new_layer
160
+
161
+ def delete_layer(self, inLayerName, forceDelete=False):
162
+ """
163
+ 레이어 삭제
164
+
165
+ Args:
166
+ inLayerName: 삭제할 레이어 이름
167
+ forceDelete: 레이어 내 객체도 함께 삭제할지 여부 (False면 기본 레이어로 이동)
168
+
169
+ Returns:
170
+ 성공 여부
171
+ """
172
+ return_val = False
173
+ deflayer = rt.layermanager.getlayer(0)
174
+ deflayer.current = True
175
+
176
+ # 레이어의 모든 노드 가져오기
177
+ nodes = self.get_nodes_by_layername(inLayerName)
178
+
179
+ if len(nodes) > 0:
180
+ if forceDelete:
181
+ # 강제 삭제 옵션이 켜져 있으면 객체도 함께 삭제
182
+ rt.delete(nodes)
183
+ nodes = rt.Array()
184
+ else:
185
+ # 아니면 기본 레이어로 이동
186
+ for item in nodes:
187
+ deflayer.addNode(item)
188
+
189
+ # 레이어 삭제
190
+ return_val = rt.LayerManager.deleteLayerbyname(inLayerName)
191
+
192
+ return return_val
193
+
194
+ def set_parent_layer(self, inLayerName, inParentName):
195
+ """
196
+ 레이어 부모 설정
197
+
198
+ Args:
199
+ inLayerName: 자식 레이어 이름
200
+ inParentName: 부모 레이어 이름
201
+
202
+ Returns:
203
+ 성공 여부
204
+ """
205
+ returnVal = False
206
+
207
+ # 타겟 레이어와 부모 레이어 가져오기
208
+ targetLayer = rt.layermanager.getlayer(self.get_layer_number(inLayerName))
209
+ parentLayer = rt.layermanager.getlayer(self.get_layer_number(inParentName))
210
+
211
+ # 두 레이어가 모두 존재하면 부모 설정
212
+ if targetLayer is not None and parentLayer is not None:
213
+ targetLayer.setParent(parentLayer)
214
+ returnVal = True
215
+
216
+ return returnVal
217
+
218
+ def rename_layer_from_index(self, inLayerIndex, searchFor, replaceWith):
219
+ """
220
+ 레이어 이름의 특정 부분을 교체
221
+
222
+ Args:
223
+ inLayerIndex: 레이어 인덱스
224
+ searchFor: 검색할 문자열
225
+ replaceWith: 교체할 문자열
226
+
227
+ Returns:
228
+ None
229
+ """
230
+ targetLayer = rt.LayerManager.getLayer(inLayerIndex)
231
+ layerName = targetLayer.name
232
+
233
+ # 문자열 찾기
234
+ find_at = layerName.find(searchFor)
235
+
236
+ # 찾은 경우 교체
237
+ if find_at != -1:
238
+ new_name = layerName.replace(searchFor, replaceWith)
239
+ targetLayer.setName(new_name)
240
+
241
+ def is_valid_layer(self, inLayerName=None, inLayerIndex=None):
242
+ """
243
+ 유효한 레이어인지 확인
244
+
245
+ Args:
246
+ inLayerName: 레이어 이름 (선택)
247
+ inLayerIndex: 레이어 인덱스 (선택)
248
+
249
+ Returns:
250
+ 유효 여부
251
+ """
252
+ layer = None
253
+
254
+ # 이름으로 확인
255
+ if inLayerName is not None:
256
+ layer = rt.LayerManager.getLayerFromName(inLayerName)
257
+ # 인덱스로 확인
258
+ elif inLayerIndex is not None:
259
+ layer = rt.LayerManager.getLayer(inLayerIndex)
260
+
261
+ # 레이어가 있으면 True, 없으면 False
262
+ return layer is not None
pyjallib/max/link.py ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Link 모듈 - 3ds Max 객체 연결 관련 기능
6
+ 원본 MAXScript의 link.ms를 Python으로 변환
7
+ """
8
+
9
+ from pymxs import runtime as rt
10
+
11
+ class Link:
12
+ """
13
+ 객체 연결(링크) 관련 기능을 위한 클래스
14
+ MAXScript의 _Link 구조체를 Python 클래스로 변환
15
+
16
+ pymxs 모듈을 통해 3ds Max의 객체 간 부모-자식 관계를 관리합니다.
17
+ """
18
+
19
+ def __init__(self):
20
+ """
21
+ 초기화 함수
22
+ """
23
+ pass
24
+
25
+ def link_to_last_sel(self):
26
+ """
27
+ 선택된 객체들을 마지막 선택 객체에 링크(부모로 지정)
28
+
29
+ Returns:
30
+ None
31
+ """
32
+ # 선택된 객체가 2개 이상인 경우에만 처리
33
+ if rt.selection.count > 1:
34
+ # 첫 번째부터 마지막 직전까지의 모든 객체를 마지막 객체에 링크
35
+ for i in range(rt.selection.count - 1):
36
+ rt.selection[i].parent = rt.selection[rt.selection.count - 1]
37
+
38
+ def link_to_first_sel(self):
39
+ """
40
+ 선택된 객체들을 첫 번째 선택 객체에 링크(부모로 지정)
41
+
42
+ Returns:
43
+ None
44
+ """
45
+ # 선택된 객체가 2개 이상인 경우에만 처리
46
+ if rt.selection.count > 1:
47
+ # 두 번째부터 마지막까지의 모든 객체를 첫 번째 객체에 링크
48
+ for i in range(1, rt.selection.count):
49
+ rt.selection[i].parent = rt.selection[0]
50
+
51
+ def unlink_selection(self):
52
+ """
53
+ 선택된 모든 객체의 부모 관계 해제
54
+
55
+ Returns:
56
+ None
57
+ """
58
+ # 선택된 객체가 있는 경우에만 처리
59
+ if rt.selection.count > 0:
60
+ # 모든 선택 객체의 부모 관계 해제
61
+ for item in rt.selection:
62
+ item.parent = None
63
+
64
+ def unlink_children(self):
65
+ """
66
+ 선택된 객체의 모든 자식 객체의 부모 관계 해제
67
+
68
+ Returns:
69
+ None
70
+ """
71
+ # 정확히 하나의 객체가 선택된 경우에만 처리
72
+ if rt.selection.count == 1:
73
+ # 선택된 객체의 모든 자식 객체의 부모 관계 해제
74
+ selObjs = rt.getCurrentSelection()
75
+ childrenObjs = selObjs[0].children
76
+ targetChildren = [child for child in childrenObjs]
77
+ for child in targetChildren:
78
+ child.parent = None
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from pymxs import runtime as rt
5
+
6
+ def jal_align_to_last():
7
+ jal.align.align_to_last_sel()
8
+
9
+ def jal_align_to_last_center():
10
+ jal.align.align_to_last_sel_center()
11
+
12
+ def jal_align_pos_only():
13
+ jal.align.align_pos_only()
14
+
15
+ def jal_align_rot_only():
16
+ jal.align.align_rot_only()
17
+
18
+ def jal_align_mirror_x():
19
+ if rt.selection.count == 0:
20
+ return False
21
+
22
+ pObj = None
23
+ defMirrorAxis = 0
24
+ oriObjArray = rt.getCurrentSelection()
25
+ boneArray = []
26
+ helperArray = []
27
+ nonBoneArray = []
28
+
29
+ mirroredBoneArray = []
30
+ mirroredHelperArray = []
31
+ mirroredNonBoneArray = []
32
+
33
+ mirroredObjArray = []
34
+
35
+ defMirrorAxis = 1
36
+
37
+ for item in oriObjArray:
38
+ if rt.classOf(item) == rt.BoneGeometry:
39
+ boneArray.append(item)
40
+ elif rt.superClassOf(item) == rt.Helper:
41
+ helperArray.append(item)
42
+ else:
43
+ nonBoneArray.append(item)
44
+
45
+ if boneArray.count != 0:
46
+ mirroredBoneArray = jal.mirror.mirror_bone(boneArray, mAxis=defMirrorAxis)
47
+ if helperArray.count != 0:
48
+ mirroredHelperArray = jal.mirror.mirror_geo(helperArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
49
+ if nonBoneArray.count != 0:
50
+ mirroredNonBoneArray = jal.mirror.mirror_geo(nonBoneArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
51
+
52
+ mirroredObjArray.extend(mirroredBoneArray)
53
+ mirroredObjArray.extend(mirroredHelperArray)
54
+ mirroredObjArray.extend(mirroredNonBoneArray)
55
+
56
+ rt.clearSelection()
57
+ rt.select(mirroredObjArray)
58
+
59
+ def jal_align_mirror_y():
60
+ if rt.selection.count == 0:
61
+ return False
62
+
63
+ pObj = None
64
+ defMirrorAxis = 0
65
+ oriObjArray = rt.getCurrentSelection()
66
+ boneArray = []
67
+ helperArray = []
68
+ nonBoneArray = []
69
+
70
+ mirroredBoneArray = []
71
+ mirroredHelperArray = []
72
+ mirroredNonBoneArray = []
73
+
74
+ mirroredObjArray = []
75
+
76
+ defMirrorAxis = 2
77
+
78
+ for item in oriObjArray:
79
+ if rt.classOf(item) == rt.BoneGeometry:
80
+ boneArray.append(item)
81
+ elif rt.superClassOf(item) == rt.Helper:
82
+ helperArray.append(item)
83
+ else:
84
+ nonBoneArray.append(item)
85
+
86
+ if boneArray.count != 0:
87
+ mirroredBoneArray = jal.mirror.mirror_bone(boneArray, mAxis=defMirrorAxis)
88
+ if helperArray.count != 0:
89
+ mirroredHelperArray = jal.mirror.mirror_geo(helperArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
90
+ if nonBoneArray.count != 0:
91
+ mirroredNonBoneArray = jal.mirror.mirror_geo(nonBoneArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
92
+
93
+ mirroredObjArray.extend(mirroredBoneArray)
94
+ mirroredObjArray.extend(mirroredHelperArray)
95
+ mirroredObjArray.extend(mirroredNonBoneArray)
96
+
97
+ rt.clearSelection()
98
+ rt.select(mirroredObjArray)
99
+
100
+
101
+ macroScript_Category = "jalTools"
102
+
103
+ rt.jal_align_to_last = jal_align_to_last
104
+ rt.macros.new(
105
+ macroScript_Category,
106
+ "jal_align_to_last",
107
+ "Align to last",
108
+ "Align to last",
109
+ "jal_align_to_last()"
110
+ )
111
+
112
+ rt.jal_align_to_last_center = jal_align_to_last_center
113
+ rt.macros.new(
114
+ macroScript_Category,
115
+ "jal_align_to_last_center",
116
+ "Align to last center",
117
+ "Align to last center",
118
+ "jal_align_to_last_center()"
119
+ )
120
+
121
+ rt.jal_align_pos_only = jal_align_pos_only
122
+ rt.macros.new(
123
+ macroScript_Category,
124
+ "jal_align_pos_only",
125
+ "Align position only",
126
+ "Align position only",
127
+ "jal_align_pos_only()"
128
+ )
129
+
130
+ rt.jal_align_rot_only = jal_align_rot_only
131
+ rt.macros.new(
132
+ macroScript_Category,
133
+ "jal_align_rot_only",
134
+ "Align rotation only",
135
+ "Align rotation only",
136
+ "jal_align_rot_only()"
137
+ )
138
+
139
+ rt.jal_align_mirror_x = jal_align_mirror_x
140
+ rt.macros.new(
141
+ macroScript_Category,
142
+ "jal_align_mirror_x",
143
+ "Mirror X",
144
+ "Mirror X",
145
+ "jal_align_mirror_x()"
146
+ )
147
+
148
+ rt.jal_align_mirror_y = jal_align_mirror_y
149
+ rt.macros.new(
150
+ macroScript_Category,
151
+ "jal_align_mirror_y",
152
+ "Mirror Y",
153
+ "Mirror Y",
154
+ "jal_align_mirror_y()"
155
+ )