Created
May 29, 2020 14:18
-
-
Save green224/12bbb72be01f7f24a18f6a096d9fd107 to your computer and use it in GitHub Desktop.
UV展開用のこまごました機能集
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
UV展開用のこまごました機能集 | |
ショートカットキー | |
i: 選択頂点を整列 | |
""" | |
import bpy | |
import os | |
import subprocess | |
import bmesh | |
from mathutils import * | |
import math | |
from bpy_extras.io_utils import ImportHelper | |
from bpy.app.handlers import persistent | |
from bpy.types import ( | |
Operator, | |
Panel, | |
PropertyGroup, | |
OperatorFileListElement, | |
) | |
from bpy.props import ( | |
BoolProperty, | |
PointerProperty, | |
StringProperty, | |
CollectionProperty, | |
FloatProperty, | |
EnumProperty, | |
IntProperty, | |
) | |
# プラグインに関する情報 | |
bl_info = { | |
"name" : "Iz UV tools", | |
"author" : "Shu", | |
"version" : (0,1), | |
'blender': (2, 80, 0), | |
"location": "UV Image Editor > Tools", | |
"description" : "Add some tools of uv", | |
"warning" : "", | |
"wiki_url" : "", | |
"tracker_url" : "", | |
"category" : "UV" | |
} | |
#------------------------------------------------------- | |
# 現在選択しているUV頂点のリストを得る | |
def getSelectedUVVerts(): | |
result = set() | |
objs = set(bpy.context.selected_objects) | |
objDatas = set([i.data for i in objs]) | |
for mesh in objDatas: | |
sel_loops = [] | |
for idx, dat in enumerate(mesh.uv_layers.active.data): | |
if (dat.select): | |
result.add( dat ) | |
return result | |
# UV頂点リストから、座標の最小値、最大値、サイズ、中央値を求める | |
def getMinMaxUV(uvs): | |
minUV = Vector((math.inf, math.inf)) | |
maxUV = Vector((-math.inf, -math.inf)) | |
for i in uvs: | |
minUV.x = min(i.x, minUV.x) | |
minUV.y = min(i.y, minUV.y) | |
maxUV.x = max(i.x, maxUV.x) | |
maxUV.y = max(i.y, maxUV.y) | |
return (minUV, maxUV, maxUV-minUV, (minUV+maxUV)/2) | |
# UV頂点リストから、座標が最も散らばっている方向を8方向から算出する | |
def getMostScatteredDir(uvs, withDiag): | |
minmaxPara = getMinMaxUV(uvs); | |
if withDiag: | |
minmaxDiag = getMinMaxUV([Vector((i.x+i.y,i.y-i.x)) for i in uvs]); | |
dir0 = minmaxPara[2].x / (minmaxPara[2].y+0.00000001) | |
dir1 = minmaxPara[2].y / (minmaxPara[2].x+0.00000001) | |
if withDiag: | |
dir2 = minmaxDiag[2].x / (minmaxDiag[2].y+0.00000001) | |
dir3 = minmaxDiag[2].y / (minmaxDiag[2].x+0.00000001) | |
if dir0<dir3 and dir1<dir3 and dir2<dir3: return "\\" | |
if dir0<dir2 and dir1<dir2: return "/" | |
if dir0<dir1: return "|" | |
return "-" | |
class Izb_Align_UV(Operator): | |
bl_idname = "object.izb_align_uv" | |
bl_label = "Iz UV Tools: Align" | |
bl_options = {'REGISTER', 'UNDO'} | |
dir: bpy.props.EnumProperty(name="dirType", | |
default="Auto", | |
items=[ | |
('Auto','Auto','Auto'), | |
('AutoWithDiag','AutoWithDiag','AutoWithDiag'), | |
('-','-','-'), | |
('|','|','|'), | |
('/','/','/'), | |
('\\','\\','\\'), | |
], | |
options={'HIDDEN'}) | |
def execute(self, context): | |
bpy.ops.object.mode_set(mode='OBJECT') | |
self.proc( context.scene.iz_uv_tool_property ) | |
bpy.ops.object.mode_set(mode='EDIT') | |
return {'FINISHED'} | |
def proc(self, param): | |
uvVerts = getSelectedUVVerts() | |
# UV座標のみの処理用配列を生成 | |
uvLst = [i.uv for i in uvVerts] | |
dctUVs = [] # 重複を除いたUV座標リスト | |
for i in uvLst: | |
if (not i in dctUVs): dctUVs.append(i.copy()) | |
# 対象頂点が1以下の場合は何もしない | |
if len(dctUVs) <= 1: return | |
# 整列方向の自動算出の場合は、方向を決定する | |
procDir = self.dir | |
if (procDir == "Auto" or procDir == "AutoWithDiag"): | |
procDir = getMostScatteredDir(dctUVs, procDir == "AutoWithDiag") | |
# 斜め整列の場合は斜めに座標変換しておく | |
if (procDir=="/" or procDir=="\\"): | |
uvLst = [Vector((i.x+i.y, i.y-i.x)) for i in uvLst] | |
dctUVs = [Vector((i.x+i.y, i.y-i.x)) for i in dctUVs] | |
# 整列方向に沿って、dctUVsをソートする | |
procDirX = procDir=="-" or procDir=="/" | |
if procDirX : dctUVs.sort(key=lambda i: i.x) | |
else : dctUVs.sort(key=lambda i: i.y) | |
# 元UVリストから、dctUVsのインデックスへのマップを構築 | |
src2dctMap = [] | |
for i in uvLst: | |
src2dctMap.append( next(idx for idx,j in enumerate(dctUVs) if (j==i)) ) | |
print("----------") | |
print(len(uvLst)) | |
print(len(dctUVs)) | |
# for i in src2dctMap: | |
for i in dctUVs: | |
print(i) | |
# ここで中央を計算しておく | |
center = getMinMaxUV(dctUVs)[3] | |
# 頂点間距離を維持した形を計算する | |
kpdUVs = [dctUVs[0].copy()] | |
for i in range( 1, len(dctUVs) ): | |
a = dctUVs[i-1] | |
b = dctUVs[i] | |
l = (b-a).length | |
c = kpdUVs[i-1] | |
if procDirX: kpdUVs.append( Vector((c.x+l, a.y)) ) | |
else: kpdUVs.append( Vector((a.x, c.y+l)) ) | |
delta = ( (kpdUVs[-1]-kpdUVs[0]) - (dctUVs[-1]-dctUVs[0]) ) / 2 | |
kpdUVs = [i-delta for i in kpdUVs] | |
# パラメータに応じて頂点間を保つ形とそうでない形の間をとる | |
kdRate = param.keep_dist_rate | |
for i in range(len(dctUVs)): | |
dctUVs[i] = dctUVs[i].lerp(kpdUVs[i], kdRate) | |
# XもしくはYに整列処理 | |
if procDirX: | |
for i in dctUVs: i.y = center.y | |
else: | |
for i in dctUVs: i.x = center.x | |
# 斜め整列の場合は元の座標に座標変換して戻す | |
if (procDir is "/" or procDir is "\\"): | |
for i in dctUVs: | |
x1 = (i.x - i.y)/2 | |
y1 = (i.x + i.y)/2 | |
i.x = x1 | |
i.y = y1 | |
# 元のUVに反映 | |
for idx,i in enumerate(uvVerts): | |
i.uv = dctUVs[src2dctMap[idx]] | |
class IzUVToolPropertyGroup(bpy.types.PropertyGroup): | |
keep_dist_rate : bpy.props.FloatProperty( | |
name="keep distance", | |
description="Keep the distance between vertices", | |
default=0, | |
min=0, | |
max=1, | |
) | |
class UI_PT_Iz_UV_Tools_panel(Panel): | |
bl_label = " " | |
bl_space_type = "IMAGE_EDITOR" | |
bl_region_type = "UI" | |
bl_category = "IzUVTools" | |
def draw_header(self, _): | |
layout = self.layout | |
row = layout.row(align=True) | |
row.label(text ="Align") | |
def draw(self, context): | |
layout = self.layout | |
column = layout.column() | |
row = column.row() | |
param = context.scene.iz_uv_tool_property | |
prop = row.prop(param, "keep_dist_rate", slider=True) | |
column.separator() | |
row = column.row() | |
prop = row.operator(Izb_Align_UV.bl_idname, text="|") | |
prop.dir = "|" | |
prop = row.operator(Izb_Align_UV.bl_idname, text="---") | |
prop.dir = "-" | |
prop = row.operator(Izb_Align_UV.bl_idname, text="/") | |
prop.dir = "/" | |
prop = row.operator(Izb_Align_UV.bl_idname, text="\\") | |
prop.dir = "\\" | |
row = column.row() | |
prop = row.operator(Izb_Align_UV.bl_idname, text="Auto") | |
prop.dir = "Auto" | |
prop = row.operator(Izb_Align_UV.bl_idname, text="Auto With Diag") | |
prop.dir = "AutoWithDiag" | |
classes = ( | |
UI_PT_Iz_UV_Tools_panel, | |
IzUVToolPropertyGroup, | |
Izb_Align_UV, | |
) | |
#------------------------------------------------------- | |
# ショートカットキー登録 | |
addon_keymaps = [] | |
def register_shortcut(): | |
wm = bpy.context.window_manager | |
kc = wm.keyconfigs.addon | |
if kc: | |
km = kc.keymaps.new( | |
"Image Generic", | |
space_type='IMAGE_EDITOR', | |
region_type='WINDOW' | |
) | |
kmi = km.keymap_items.new( | |
Izb_Align_UV.bl_idname, | |
'I', | |
'PRESS', | |
shift=False, | |
ctrl=False, | |
alt=False | |
) | |
kmi.properties.dir = "Auto" | |
kmi.active = True | |
addon_keymaps.append((km, kmi)) | |
# ショートカットキー登録解除 | |
def unregister_shortcut(): | |
for km, kmi in addon_keymaps: km.keymap_items.remove(kmi) | |
addon_keymaps.clear() | |
#------------------------------------------------------- | |
# プラグインをインストールしたときの処理 | |
def register(): | |
for cls in classes: | |
bpy.utils.register_class(cls) | |
bpy.types.Scene.iz_uv_tool_property = bpy.props.PointerProperty(type=IzUVToolPropertyGroup) | |
register_shortcut() | |
# プラグインをアンインストールしたときの処理 | |
def unregister(): | |
unregister_shortcut() | |
for cls in reversed(classes): | |
bpy.utils.unregister_class(cls) | |
del bpy.types.Scene.iz_uv_tool_property | |
if __name__ == "__main__": | |
register() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment