About
home

Python으로 모델링 정리 2. 배 밧줄

목표. Goal

샘플의 와이어는 실린더나 박스형태의 폴리곤으로 되어있습니다. 이것을 spline으로 변경하고 Sweep을 이용한 구조로 수정합니다.

샘플 출처. Sample link

파일 정리. Organize unnecessary content

FBX를 불러오면 모델링 하나와 많은 null 이 있습니다. 모델링만 남기도 다 지워주세요.
모델링은 Polygon Group to Object 로 분리하고 JS Random Display Color 를 이용해서 색을 변경해주세요. 이중 와이어 정리를 해볼 것이기에 와이어만 골라내 주세요.
wire_only.zip
121.4KB

스크립트 실행 방법. How to run script.

스크립트 실행 과정. Process

1.
오브젝트의 한 포인트를 기준으로 다른 포인트들과의 거리를 구합니다. (0과1의 거리), (0과2의 거리), (0과3의 거리)...
2.
경우의 수만큼 두 점간의 거리를 비교하여 0.5cm 보다 작다면 하나의 그룹으로 판단할 수 있습니다. [0,1,2,3,4], [5,6,7,8,9]...
3.
그렇게 찾아진 그룹들의 포인트 위치값을 모두 더하고 포인트 수로 나누면 중심위치값이 나옵니다. 포인트 포지션은 c4d.Vector(0,0,0) 타입이고 더하고 나누는 과정에서 같은 성분끼리 계산합니다. 위 그림으로 보면 총 5개의 그룹을 찾을수 있고, 5개의 중심점의 위치를 찾습니다.
4.
찾은 값은 Z 값을 기준으로 정렬하고, X 값을 기준으로 다시 정리합니다. 정렬을 하지 않으면 스플라인 생성시 꼬인 모양이 됩니다. 이 정렬로 완벽히 해결되진 않지만, 대부분 정상적인 스플라인이 생성됩니다.
5.
찾은 중심점의 수와 위치값을 기반으로 Spline Object 를 생성합니다.
6.
1~5까지의 과정을 와이어 오브젝트 수 만큼 반복시킵니다.
7.
꼬여있는 스플라인을 찾아 다시한번 정렬합니다.

정리 코드. Final code

줄 오브젝트를 선택하고 스크립트를 실행해야합니다.
import c4d def main(): # 선택한 오브젝트들 검색, 하위 검색은 하지 않습니다. sels = doc.GetActiveObjects(1) # 선택이 없다면 중지합니다. if sels == []:return # 언두 기록을 시작합니다. doc.StartUndo() # 스플라인을 묶어줄 널을 생성합니다. null = c4d.BaseObject(c4d.Onull) null.SetName('wire') doc.InsertObject(null) # 널 생성을 언두에 기록합니다. Add 언두로 언두할 내용을 기록해야 합니다. doc.AddUndo(c4d.UNDOTYPE_NEW, null) # 가까운 점을 검색->중심->스프라인생성->널에 차일드 # 선택한 오브젝트를 하나씩 sel 에 넣어주고 아래 코드를 실행합니다. for sel in sels: # 폴리곤 오브젝트가 아니라면 아래 코드를 스킵합니다. 널은 스킵 # continue 가 아래로 계속 코드를 실행하는것이 아닙니다. 패스의 의미입니다. if sel.GetType() != c4d.Opolygon: continue # 폴리곤 오브젝트면 아래코드들이 실행됩니다. # 모든 포인트 위치값을 ps 에 리스트로 담습니다. ps = sel.GetAllPoints() # 무한 반복을 막기위해 반복 회수를 카운트합니다. 시작은 0으로 합니다. loop = 0 # 그룹으로 찾을 포지션을 담을 빈 리스트입니다. pos = [] # 반복을 시작합니다. ps 에 내용이 있을때는 True, ps = [] 비어있으면 False while ps: # 30번 이상 반복중이라면 중지합니다. if loop > 30: return # 비교대상인 포지션 하나를 first 로 지정합니다. first = ps.pop(0) # first 를 포함한 그룹을 담을 리스트를 만듭니다. find_near = [first] # i 에 역으로 숫자를 집어넣어 pop 이용시 오류가 없도록 만든다. # pop 은 리스트에서 해당 값을 뽑아내고 리스트에서 제거한다. # 따라서 뒤에서 앞으로 검사를 하고 뽑아내면 아직 비교하지 않은 앞의 값에는 영향이 없다. for i in reversed(range(len(ps))): # 두점 사이의 거리 dis = (first-ps[i]).GetLength() # 적당한 거리 값을 찾아 비교값으로 사용한다. if dis < 0.5: # 거리가 맞으면 리스트에 담아주면서 ps에서 제거한다. find_near.append(ps.pop(i)) # 찾은 점들의 위치를 평균내면 점들의 중심 찾을 수 있다. pos.append(sum(find_near)/len(find_near)) # 실행값을 1 올려 기록한다. loop += 1 # 비슷한 위치의 점들이 한번 걸러진 상태이고 ps 에는 그외의 것들만 남아있다. # 다시 위의 과정을 반복하면서 ps 의 값들이 전부 빠지면 반복을 중지하게 된다. # 중심값들을 정렬한다. pos.sort(key=lambda x : (x[2], x[0])) # 한 오브젝트에서 찾은 중심위치 값을 기반으로 스플라인 을 생성한다. spline = c4d.SplineObject(len(pos), 1) spline.ResizeObject(len(pos),1) spline.SetSegment(0, len(pos), False) spline.SetAllPoints(pos) spline[c4d.SPLINEOBJECT_TYPE] = 0 spline.Message(c4d.MSG_UPDATE) spline.InsertUnder(null) spline.SetMg(sel.GetMg()) # 추가된 스플라인을 언두로 추가한다. doc.AddUndo(c4d.UNDOTYPE_NEW, spline) # 언두를 닫아준다. 언두는 많은 AddUndo도 Start부터 End까지가 한번의 언두이다. doc.EndUndo() c4d.EventAdd() if __name__ == "__main__": main()
Python
생성된 와이어 스플라인
꼬여있는 일부 스플라인

꼬인 스플라인 수정 코드. Align point index order

꼬여있는 스플라인 오브젝트만 선택하고 실행합니다. X 위치값으로 포인트 인덱스를 수정합니다.
import c4d def main(): sel = doc.GetActiveObjects(1) for s in sel: pos = s.GetAllPoints() pos.sort(key=lambda x : x[0]) s.SetAllPoints(pos) s.Message(c4d.MSG_UPDATE) c4d.EventAdd() if __name__ == "__main__": main()
Python
스플라인으로 변환한 와이어는 스플라인 다이나믹을 이용하기도 쉽고 모델링을 관리하는 측면에서도 유리한 면이 습니다.

스크립트 시연. Demo