Skip to content

Instantly share code, notes, and snippets.

@ShikouYamaue
Last active December 17, 2017 08:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShikouYamaue/7bed38e3f8cc48c10b9b1b83e75621be to your computer and use it in GitHub Desktop.
Save ShikouYamaue/7bed38e3f8cc48c10b9b1b83e75621be to your computer and use it in GitHub Desktop.
Dijkstras Sphere
# -*- coding: utf-8 -*-
from maya import cmds
from maya import OpenMayaUI
from collections import OrderedDict
import maya.api.OpenMaya as om
import math
import copy
import time
import numpy as np
#PySide2、PySide両対応
import imp
try:
imp.find_module('PySide2')
from PySide2.QtWidgets import *
from PySide2.QtGui import *
from PySide2.QtCore import *
except ImportError:
from PySide.QtGui import *
from PySide.QtCore import *
class Option():
def __init__(self):
self.window = MainWindow(getMayaWindow())
self.window.resize(250, 65)
self.window.show()
global start_vtx
global distance_dict
global script_job
global distance
if 'script_job' in globals():
pass
else:
script_job = None
class MainWindow(QMainWindow):
def __init__(self, parent = None):
global start_vtx
global distance_dict
global script_job
super(MainWindow, self).__init__(parent)
try:
cmds.delete('pSphere*')
except:
pass
self.mesh = cmds.polySphere(cuv=1, sy=40, ch=0, sx=60, r=10, ax=(0, 1, 0))[0]
cmds.delete(self.mesh+'.f[1189:1199]')
cmds.selectMode(co=True)
wrapper = QWidget()
self.setCentralWidget(wrapper)
self.main_layout = QVBoxLayout()
wrapper.setLayout(self.main_layout)
#-----------------------------------------------------------------------
label = QLabel('Dijkstras', self)
self.main_layout.addWidget(label)
self.main_layout.addWidget(make_h_line())
self.scale_layout = QHBoxLayout()
self.main_layout.addLayout(self.scale_layout)
#タイトルラベル
label = QLabel('Max Distance',self)
self.scale_layout.addWidget(label)
#スケールボタン追加
button = QPushButton('Compute Dijkstras')
button.clicked.connect(Callback(self.compute_dijkstras))
self.main_layout.addWidget(button)
self.distance = QDoubleSpinBox(self)#スピンボックス
self.distance.setRange(0, 100)
self.distance.setValue(10.0)#値を設定
self.distance.setDecimals(2)#小数点桁数設定
self.scale_layout.addWidget(self.distance)
#スライダバーを設定
self.distance_sld = QSlider(Qt.Horizontal,self)
self.distance_sld.setRange(0, 10000)
self.distance_sld.setValue(self.distance.value())
self.scale_layout.addWidget(self.distance_sld)
#スライダーとボックスの値をコネクト。連動するように設定。
self.distance_sld.valueChanged.connect(lambda:self.distance.setValue(self.distance_sld.value()/100.0))
self.distance.editingFinished.connect(lambda:self.distance_sld.setValue(float(self.distance.value())*100))
self.distance.valueChanged.connect(self.set_global_param)
global distance
global pre_distance
distance = self.distance.value()
pre_distance = self.distance.value()
def compute_dijkstras(self):
global start_vtx
global distance_dict
global pre_distance
global vtx_pos_dict
sel_vtx = cmds.polyListComponentConversion(cmds.ls(sl=True), tv=True)
sel_vtx = cmds.filterExpand(sel_vtx, sm=31)[0]
self.remove_job()
targetMesh = self.mesh
start_vtx = targetMesh+'.'+sel_vtx.split('.')[-1]
cmds.select(start_vtx, r=True)
#メッシュの全頂点を取得しておく
self.all_vtx = cmds.polyListComponentConversion(targetMesh, tv=True)
self.all_vtx = cmds.filterExpand(self.all_vtx, sm=31)
vtx_pos_dict = {vtx:np.array(cmds.xform(vtx, q=True, t=True, ws=True)) for vtx in self.all_vtx}
goal_vtx = self.all_vtx[:]
goal_vtx.remove(start_vtx)
goal_vtx = goal_vtx[-1]
#全頂点のつながり辞書を作っておく、頂点をエッジ→頂点変換すると周辺の頂点が取れる現象を応用。
temp_Dict = {}
for vtx in self.all_vtx:
around = cmds.polyListComponentConversion(vtx, tf=True)
around = cmds.polyListComponentConversion(around, tv=True)
around = cmds.filterExpand(around, sm=31)
around.remove(vtx)
roundList = []
a = om.MPoint(vtx_pos_dict[vtx])
for rVtx in around:
b = om.MPoint(vtx_pos_dict[rVtx])
dist = (a-b).length()
roundList.append((rVtx, dist))
temp_Dict[vtx] = roundList
#スタートとゴールの経路を保持するためオーダードディクトを利用
routeDict = OrderedDict()
#スタートとゴールを最初と最後のキーと要素に設定する。あとは適当。
routeDict[start_vtx] = temp_Dict[start_vtx]
for k in temp_Dict.keys():
if k != start_vtx and k != goal_vtx:
routeDict[k] = temp_Dict[k]
routeDict[goal_vtx] = temp_Dict[goal_vtx]
#探索用変数を色々初期化
nodeNum = len(routeDict)#ノードの総数
distance_dict = {vtx:float('inf') for vtx in self.all_vtx}#ノードごとの距離のリスト、初期値無限大を与える
previous_nodes_dict = {vtx:'' for vtx in self.all_vtx}#最短経路でそのノードのひとつ前に到達するノードの対応辞書
distance_dict[start_vtx] = 0#初期のノードの距離は0とする
unsearched_nodes = copy.copy(distance_dict)#未探索ノード
i = 0
while(len(unsearched_nodes) != 0): #未探索ノードがなくなるまで繰り返す
#未探索ノードのうちで最小のindex番号を取得
min_dist = min(unsearched_nodes.values())
min_id = unsearched_nodes.values().index(min_dist)
target_min_key = unsearched_nodes.keys()[min_id]
#未探索ノードから除去しておく
unsearched_nodes.pop(target_min_key)
#ターゲットノードの周辺ノード取得
round_vertex = routeDict[target_min_key]
#周辺ノードへのスタートからの到達距離を比較、以前設定した距離より小さい場合は更新する。
for rVtx in round_vertex:
i += 1
real_dist = distance_dict[target_min_key] + rVtx[1]#実際の距離
if distance_dict[rVtx[0]] > real_dist:
distance_dict[rVtx[0]] = real_dist
unsearched_nodes[rVtx[0]] = real_dist
#そのノードに最短で到達するための一つまえのノード辞書を更新しておく
previous_nodes_dict[rVtx[0]] = target_min_key
self.create_job()
def set_global_param(self):
global distance
distance = self.distance.value()
def create_job(self):
global start_vtx
global script_job
global base_pos
global pre_pos
global pre_distance
global distance
distance = self.distance.value()
pre_distance = self.distance.value()
if script_job is None:
base_pos = np.array(cmds.xform(start_vtx, q=True, t=True, ws=True))
pre_pos = base_pos
script_job = cmds.scriptJob(e=("idle", "move_with_cost_ratio()"), kws=False)
def closeEvent(self, e):
self.remove_job()
def remove_job(self):
global script_job
if script_job is not None:
print 'kill job'
cmds.scriptJob(k=script_job)
script_job = None
def move_with_cost_ratio():
global start_vtx
global distance_dict
global pre_pos
global distance
global pre_distance
global vtx_pos_dict
if distance == 0.0:
[cmds.xform(vtx, t=vtx_pos_dict[vtx], ws=True) for vtx in distance_dict.keys()]
return
pos = np.array(cmds.xform(start_vtx, q=True, t=True, ws=True))
if str(pos) != str(pre_pos) or distance!= pre_distance:
moved = pos - base_pos
for vtx, cost in distance_dict.items():
if vtx == start_vtx:
continue
if cost > distance:
cmds.xform(vtx, t=vtx_pos_dict[vtx], ws=True)
continue
ratio = (distance - cost)/distance
new_pos = vtx_pos_dict[vtx]+moved*ratio
cmds.xform(vtx, t=new_pos, ws=True)
pre_pos = pos
pre_distance = distance
class Callback(object):
def __init__(self, func, *args, **kwargs):
self.__func = func
self.__args = args
self.__kwargs = kwargs
def __call__(self, *args, **kwargs):
cmds.undoInfo(openChunk=True)
try:
return self.__func(*self.__args, **self.__kwargs)
except:
raise
finally:
cmds.undoInfo(closeChunk=True)
def getMayaWindow():
try:
imp.find_module("shiboken2")
import shiboken2
return shiboken2.wrapInstance(long(OpenMayaUI.MQtUtil.mainWindow()), QWidget)
except ImportError:
import shiboken
return shiboken.wrapInstance(long(OpenMayaUI.MQtUtil.mainWindow()), QWidget)
def make_h_line():
hline = QFrame()
hline.setFrameShape(QFrame.HLine)
hline.setFrameShadow(QFrame.Sunken)
return hline
Option()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment