Edited at

[Cinema 4D] スプラインのバウンディングボックスを作成してみる

前回の記事でポリゴンオブジェクトからワールド座標に平行なバウンディングボックスを作成することができましたが,今回は前回のスクリプトを少し拡張してCinema 4Dでよく使うスプラインオブジェクトからバウンディングボックスを作成してみようと思います.

スプラインはポリゴンオブジェクトとは異なるタイプのオブジェクトなので,if文で条件分岐させてあげますが,少し注意が必要です.

例えば,ポリゴンオブジェクトの場合はobj.IsInstanceOf(c4d.Opolygon)などでタイプをチェックできます.

InstanceOfのマニュアル

これでIsInstanceOf(c4d.Ospline)でスプラインタイプがチェックできるかと思いきやこれはできません.

スプラインタイプの場合は,次のようにしてあげます.他にGetType() == c4d.Osplineでもできます.InstanceOfだとできないんですね.

if obj.GetInfo() & c4d.OBJECT_ISSPLINE:

realSpline = obj.GetRealSpline()
if realSpline is False:
return
# Do something

2行目のGetRealSpline()でプリミティブスプラインから編集可能にした状態のスプラインを取得します.

GetRealSpline()

これで準備ができたので,あとはポリゴンオブジェクトと同様にrealSpline.GetClone()でクローンを取得してそれに対して逆行列をかけてワールド座標に平行にしたのち,各ポイントに回転行列を乗算して元の位置にもどしてあげます.前回と全く同じ手順です.

できました

20190303-1.jpg

と言いたいところですが,接線の端までバウンディングボックスができてしまっています.これはちょっとよくないですね.どうやらGetRad()は接線の端まで含んだ半径を返すようです.

そこで,このクローンスプラインに対して〈現在の状態をオブジェクト化〉をして内部で生成している実際のポイントを取得していきます.英語ではCurrent State to Objectです.Plugincafeでもこのトピックは沢山みつかるので,検索してみると良いでしょう.

オブジェクトに対してutils.SendModelingCommandを実行するわけですが,先にモジュールをインポートしておきます.

from c4d import utils


rs = obj.GetRealSpline()
if rs is False:
return

clone = rs.GetClone()
clone.SetMg(~obj.GetMg() * obj.GetMg())
settings = c4d.BaseContainer()
res = utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,
list = [clone],
mode = c4d.MODELINGCOMMANDMODE_ALL,
bc = settings,
doc = doc)
c4d.EventAdd()

if res is False:
print "Something went wrong."
elif res is True:
print "Command successful."
elif isinstance(res, list):
print "Here you get the newly created object(s)."

[utils.SendModelingCommand]のマニュアル(https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d.utils/index.html?highlight=sendmodelingcommand#c4d.utils.SendModelingCommand)

commandは実行するコマンドです.

引数のオブジェクトはリストになっているので,[clone]としています.

modeはALLのほか,選択範囲に限定したりできます.

settingsは実行コマンドのパラメータを指定できます.例えばID_MODELING_EXTRUDE_TOOLを使う場合に50cm押し出したい時などです.〈現在の状態をオブジェクト化〉では不要ですが.

注意点としては,複製したオブジェクトを渡さないとシーン内のスプラインに対してコマンドが実行されてしまいます.したがって,この時[clone]を渡しています.

また,返り値はリストになるので,実行後のオブジェクトへはres[0].GetPointCount()などとしてアクセスします.

関数は次のようにしてみました.引数のobjはすでに記事冒頭のobj.GetInfo() & c4d.OBJECT_ISSPLINE:でスプラインオブジェクトだけを渡すようにしていますので,関数内でスプラインかどうかのチェックはしていません.

def createBoundingBoxFromSpline(obj):

print "Call createBoundingBox from Spline"

# もしスプラインプリミティブならスプライン状態を取得する(編集可能にした状態)
rs = obj.GetRealSpline()
if rs is False:
return

# クローンをとる
clone = rs.GetClone()
clone.SetMg(~obj.GetMg() * obj.GetMg())

# 現在の状態をオブジェクト化
settings = c4d.BaseContainer()
res = utils.SendModelingCommand(command = c4d.MCOMMAND_CURRENTSTATETOOBJECT,
list = [clone],
mode = c4d.MODELINGCOMMANDMODE_ALL,
bc = settings,
doc = doc)
c4d.EventAdd()

if res is False:
print "Something went wrong."
elif res is True:
print "Command successful."
elif isinstance(res, list):
print "Here you get the newly created object(s)."

# ポイントに元の行列を乗算してポイントだけ元の位置に戻す
if res[0].GetPointCount():
pcount = res[0].GetPointCount()
points = res[0].GetAllPoints()
for p in xrange(pcount):
res[0].SetPoint(p, obj.GetMg() * points[p])
print points[p]
res[0].Message(c4d.MSG_UPDATE)
c4d.EventAdd()

# XYZの最小,最大値を取得する
center = res[0].GetMp()
xMin = center.x + res[0].GetRad().x * -1
xMax = center.x + res[0].GetRad().x
yMin = center.y + res[0].GetRad().y * -1
yMax = center.y + res[0].GetRad().y
zMin = center.z + res[0].GetRad().z * -1
zMax = center.z + res[0].GetRad().z
print xMin
print xMax
print yMin
print yMax
print zMin
print zMax

# XYZの最小・最大値からバウンディングボックスを生成する
box = c4d.BaseObject(c4d.Opolygon)
box.ResizeObject(8,6)
box.SetPoint(0,c4d.Vector(xMin, yMin, zMin))
box.SetPoint(1,c4d.Vector(xMin, yMax, zMin))
box.SetPoint(2,c4d.Vector(xMax, yMin, zMin))
box.SetPoint(3,c4d.Vector(xMax, yMax, zMin))
box.SetPoint(4,c4d.Vector(xMax, yMin, zMax))
box.SetPoint(5,c4d.Vector(xMax, yMax, zMax))
box.SetPoint(6,c4d.Vector(xMin, yMin, zMax))
box.SetPoint(7,c4d.Vector(xMin, yMax, zMax))
box.SetPolygon(0, c4d.CPolygon(0,1,3,2))
box.SetPolygon(1, c4d.CPolygon(2,3,5,4))
box.SetPolygon(2, c4d.CPolygon(4,5,7,6))
box.SetPolygon(3, c4d.CPolygon(6,7,1,0))
box.SetPolygon(4, c4d.CPolygon(1,7,5,3))
box.SetPolygon(5, c4d.CPolygon(6,0,2,4))
doc.InsertObject(box)

tag = c4d.BaseTag(c4d.Tdisplay)
tag[c4d.DISPLAYTAG_AFFECT_DISPLAYMODE] = True
tag[c4d.DISPLAYTAG_SDISPLAYMODE] = 6
tag[c4d.DISPLAYTAG_WDISPLAYMODE] = 0
box.InsertTag(tag)

box.Message(c4d.MSG_UPDATE)
doc.AddUndo(c4d.UNDOTYPE_NEW,box)
doc.EndUndo()
c4d.EventAdd()
return

できました

20190303-2.jpg