まぁ(必要に迫られて)そんな気分になったんですよ。
ボクセルというかキューブの集合体ですね。
ドット絵というか、マイクラというか、そういう奴
MASHでできるんですけどもねぇ
考える
- BBOXを取得
- BBOXをcubeで埋め尽くす(cubeのサイズは指定できるように)
- cubeを仕分けする(完全にスフィア外、一部スフィア外、全てスフィア内)
- 完全にスフィア外は削除。 一部スフィア外を削除するかはオプション
こんな感じかなぁ
実装
まずはBBOXをcubeで満たそう
def fillBBox(cubeSize,target):
#getTargetBBox
bbox = cmds.xform(target, q =True,boundingBox =True,ws=True)
#bboxのサイズがcubeのサイズの倍数になるように加工
extendX = ((bbox[3] - bbox[0]) - ((bbox[3] - bbox[0]) / cubeSize)*cubeSize )*0.5
extendY = ((bbox[4] - bbox[1]) - ((bbox[4] - bbox[1]) / cubeSize)*cubeSize )*0.5
extendZ = ((bbox[5] - bbox[2]) - ((bbox[5] - bbox[2]) / cubeSize)*cubeSize )*0.5
## minX Y Z maxX Y Z
##ピッタリすぎないように念のためのりしろを追加
bbox = [
bbox[0] - extendX - cubeSize,
bbox[1] - extendY - cubeSize,
bbox[2] - extendZ - cubeSize,
bbox[3] + extendX + cubeSize,
bbox[4] + extendY + cubeSize,
bbox[5] + extendZ + cubeSize,
]
#cubeを配置する為に 数とスタート位置を取得
numX = int((bbox[3] - bbox[0]) / cubeSize)
numY = int((bbox[4] - bbox[1]) / cubeSize)
numZ = int((bbox[5] - bbox[2]) / cubeSize)
startX = bbox[0] + cubeSize*0.5
startY = bbox[1] + cubeSize*0.5
startZ = bbox[2] + cubeSize*0.5
for i in range(0,numY):
for ii in range(0,numX):
for iii in range(0,numZ):
cube = cmds.polyCube(ch =False,w = cubeSize,h = cubeSize, d = cubeSize)[0]
cmds.setAttr(cube + ".tx",startX + (cubeSize*ii))
cmds.setAttr(cube + ".ty",startY + (cubeSize*i))
cmds.setAttr(cube + ".tz",startZ + (cubeSize*iii))
fillBBox(0.2,"pSphere1")
あれー? なんか偏るなぁ・・・
浮動小数点の罠
色々検証してみたところ、この辺が問題となってる。
numX = int((bbox[3] - bbox[0]) / cubeSize)
numY = int((bbox[4] - bbox[1]) / cubeSize)
numZ = int((bbox[5] - bbox[2]) / cubeSize)
うーん
ちゃんと考えないと駄目ですわね!
numX = (bbox[3] - bbox[0]) / cubeSize
#12.000001192092894
numY = (bbox[4] - bbox[1]) / cubeSize
#11.999999999999998
これを intで包んでしまったので
12.000001192092894 -> 12
11.999999999999998 -> 11
となったわけですわね。
んー 小数点第1位で四捨五入・・・かなぁ
雑ならラウンドを追加しますか。
※こういう関数名は後で絶対後悔しますね!
def theTwoRound(input):
return int((input * 2 + 1) // 2)
改めて実行
from decimal import *
def theTwoRound(input):
return int((input * 2 + 1) // 2)
def fillBBox(cubeSize,target):
#getTargetBBox
bbox = cmds.xform(target, q =True,boundingBox =True,ws=True)
#bboxのサイズがcubeのサイズの倍数になるように加工
extendX = ((bbox[3] - bbox[0]) - ((bbox[3] - bbox[0]) / cubeSize)*cubeSize )*0.5
extendY = ((bbox[4] - bbox[1]) - ((bbox[4] - bbox[1]) / cubeSize)*cubeSize )*0.5
extendZ = ((bbox[5] - bbox[2]) - ((bbox[5] - bbox[2]) / cubeSize)*cubeSize )*0.5
## minX Y Z maxX Y Z
bbox = [
bbox[0] - extendX - cubeSize,
bbox[1] - extendY - cubeSize,
bbox[2] - extendZ - cubeSize,
bbox[3] + extendX + cubeSize,
bbox[4] + extendY + cubeSize,
bbox[5] + extendZ + cubeSize,
]
#cubeを配置する為に 数とスタート位置を取得
numX = theTwoRound((bbox[3] - bbox[0]) / cubeSize)
numY = theTwoRound((bbox[4] - bbox[1]) / cubeSize)
numZ = theTwoRound((bbox[5] - bbox[2]) / cubeSize)
startX = bbox[0] + cubeSize*0.5
startY = bbox[1] + cubeSize*0.5
startZ = bbox[2] + cubeSize*0.5
for i in range(0,numY):
for ii in range(0,numX):
for iii in range(0,numZ):
cube = cmds.polyCube(ch =False,w = cubeSize,h = cubeSize, d = cubeSize)[0]
cmds.setAttr(cube + ".tx",startX + (cubeSize*ii))
cmds.setAttr(cube + ".ty",startY + (cubeSize*i))
cmds.setAttr(cube + ".tz",startZ + (cubeSize*iii))
fillBBox(0.2,"pSphere1")
おうけい
あとはこっちの内容を元に各cubeの内外仕分けをすればいいかな
確認
import maya.cmds as cmds
import maya.api.OpenMaya as om
def getDagNode(target):
try:
sellist = om.MGlobal.getSelectionListByName(target)
return sellist.getDagPath(0)
except:
return None
def stringToVector(axis):
if axis == "x":
return om.MVector.kXaxisVector
elif axis == "y":
return om.MVector.kYaxisVector
elif axis == "z":
return om.MVector.kZaxisVector
elif axis == "-x":
return om.MVector.kXnegAxisVector
elif axis == "-y":
return om.MVector.kYnegAxisVector
elif axis == "-z":
return om.MVector.kZnegAxisVector
def checkVtxInside(vtx,mesh):
meshDagPath = getDagNode(mesh)
shapeFn = om.MFnMesh(meshDagPath)
position = cmds.xform(vtx , q =True , ws = True, t =True)
raySource = om.MFloatPoint(*position)
space = om.MSpace.kWorld
id_list = []
point_list = []
isInside = True
for axis in ["x","y","z","-x","-y","-z"]:
#vtxの位置から全軸方向へrayをとばす
axisVector = stringToVector(axis)
maxParam = 9999
testBothDirections = False
accelParams=om.MMeshIsectAccelParams()
result = shapeFn.closestIntersection(
raySource,
om.MFloatVector(axisVector),
space,
maxParam,
testBothDirections, idsSorted=False, accelParams=accelParams, tolerance=0.0001)
if result == None:
isInside = False
break
return isInside
def getOutsideVtx(checkTarget,clipGuide):
meshDagPath = getDagNode(checkTarget)
shapeFn = om.MFnMesh(meshDagPath)
outsideVtx = []
for i in range(0,shapeFn.numVertices):
#ターゲットのvtxを順番に処理
isInside = checkVtxInside(checkTarget +".vtx["+str(i)+"]",clipGuide)
if isInside == False:
outsideVtx.append(checkTarget +".vtx["+str(i)+"]")
return outsideVtx
def theTwoRound(input):
return int((input * 2 + 1) // 2)
def fillBBox(cubeSize,target):
#getTargetBBox
bbox = cmds.xform(target, q =True,boundingBox =True,ws=True)
#bboxのサイズがcubeのサイズの倍数になるように加工
extendX = ((bbox[3] - bbox[0]) - ((bbox[3] - bbox[0]) / cubeSize)*cubeSize )*0.5
extendY = ((bbox[4] - bbox[1]) - ((bbox[4] - bbox[1]) / cubeSize)*cubeSize )*0.5
extendZ = ((bbox[5] - bbox[2]) - ((bbox[5] - bbox[2]) / cubeSize)*cubeSize )*0.5
## minX Y Z maxX Y Z
bbox = [
bbox[0] - extendX - cubeSize,
bbox[1] - extendY - cubeSize,
bbox[2] - extendZ - cubeSize,
bbox[3] + extendX + cubeSize,
bbox[4] + extendY + cubeSize,
bbox[5] + extendZ + cubeSize,
]
#cubeを配置する為に 数とスタート位置を取得
numX = theTwoRound((bbox[3] - bbox[0]) / cubeSize)
numY = theTwoRound((bbox[4] - bbox[1]) / cubeSize)
numZ = theTwoRound((bbox[5] - bbox[2]) / cubeSize)
startX = bbox[0] + cubeSize*0.5
startY = bbox[1] + cubeSize*0.5
startZ = bbox[2] + cubeSize*0.5
cubes = []
for i in range(0,numY):
for ii in range(0,numX):
for iii in range(0,numZ):
cube = cmds.polyCube(ch =False,w = cubeSize,h = cubeSize, d = cubeSize)[0]
cmds.setAttr(cube + ".tx",startX + (cubeSize*ii))
cmds.setAttr(cube + ".ty",startY + (cubeSize*i))
cmds.setAttr(cube + ".tz",startZ + (cubeSize*iii))
cubes.append(cube)
return cubes
clipGuide = "pSphere1"
cubes = fillBBox(0.2,clipGuide)
cubeDict = {
"inside":[],
"outside":[],
"border":[]
}
for cube in cubes:
outsideVtx = getOutsideVtx(cube,clipGuide)
if len(outsideVtx) == 0:
cubeDict["inside"].append(cube)
elif len(outsideVtx) == 4:
cubeDict["outside"].append(cube)
else:
cubeDict["border"].append(cube)
cmds.delete(cubeDict["outside"])
cmds.delete(cubeDict["border"])
反省&改修案
処理が重い。
たかがsphereでも結構時間かかるので、cubeのサイズを小さくし過ぎると恐らくmayaが耐えられません。
であれば、あらかじめvtxの位置かcubeの位置をシミュレートして
別の方法でメッシュを生成すればもう少し軽くなるかしら?
まぁ
MASHで良いと思うんですけどね!