Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@green224
Created May 29, 2020 14:18
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 green224/12bbb72be01f7f24a18f6a096d9fd107 to your computer and use it in GitHub Desktop.
Save green224/12bbb72be01f7f24a18f6a096d9fd107 to your computer and use it in GitHub Desktop.
UV展開用のこまごました機能集
"""
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