OneonetのPyBE

Python,Blender,Excelのいろいろ

Oneonet Python Cheat Sheet v2022.02

これで事足りてます。不定期に更新していきます。

#■■■■■■■■■■
#Oneonet Python Cheat Sheet 2022.02版
#■■■■■■■■■■

#■■■■■■■■■■
#■マジックコメント/Magic comment
# -*- coding: utf-8 -*-

#■■■■■■■■■■
#■main
if __name__ == "__main__":
	main()

#■■■■■■■■■■
#■コメント
# This is a comment
a = 1  # initialization
"""
複数行コメント
です!
"""

#■■■■■
#■エスケープ文字
"""
\'	#Single quote
\"	#Double quote
\t	#Tab
\n	#Newline
\\	#Backslash
"""

#■■■■■■■■■■
#■アンダースコア
for _ in range(10):
x, _, _ = func()
value = 100_000_000

#■■■■■■■■■■
#■変数初期化
a , b = 0, 100
a = b = 0
del a			#削除

#■■■■■■■■■■
#■計算1
"""
+	足し算
-	引き算
*	掛け算
/	除法
//	整数除算
%	剰余
**	指数
"""

#■■■■■■■■■■
#■代入
x += 5		#x = x + 5
x -= 5		#x = x - 5
x *= 5		#x = x * 5
x /= 5		#x = x / 5
x %= 5		#x = x % 5
x ** = 5	#x = x ** 5
#---
x = [1,2,3]
x1, x2, x3 = x	#x1=1, x2=2, x3=3
#---
x, y = 1, 2
x, y = y, x		#x=2,y=1

#■■■■■■■■■■
#■文字列操作
'Hello %s' % 'world'		#'Hello world'
'Hello %d' % 100		#'Hello 100'
'Hello %x' % 100		#'Hello 64'
#---
x = 123
f'value = {x*2}'		#'value = 246
#---

#■■■■■■■■■■
#■比較
"""
==	等しい
!=	等しくない
<	未満
>	より大きい
<=	以下または等しい
>=	大きいまたは等しい
"""
#---
x = [1,2,3,4,5]
x1 = (3 in x)		#True
x2 = (10 in x)		#False
x3 = (3 not in x)	#False
x4 = (10 not in x)	#True

#■■■■■■■■■■
#■計算2
ルート:
#---
import math
x = math.sqrt(5)
#---
import numpy
x = numpy.sqrt(5)
degrees<->radians:
#---
import math
x = math.degrees(math.pi/2)	#Rad to Deg
x = math.radians(90)		#Deg to Rad

#■■■■■■■■■■
#■リスト / スライス
x = [1,2,3,4,5] #xの初期値は常にこの値として・・・
x               #[1, 2, 3, 4, 5]
x[0]            #1
x[3]            #4
x[-1]           #5
x[-3]           #3
x[1:3]          #[2, 3]
x[1:]           #[2, 3, 4, 5]
x[:2]           #[1, 2]  ***
x[1:3]          #[2, 3]
x[0:-1]         #[1,2,3,4]  ***
x[::-1]         #[5, 4, 3, 2, 1]
len(x)          #5
x.append(10)    #[1, 2, 3, 4, 5, 10]
x.index(4)      #3
x.remove(4)     #[1, 2, 3, 5]
x.insert(1,100) #[1, 100, 2, 3, 4, 5]
del x[4]        #[1, 2, 3, 4]
x = x + x       #[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
x = x * 3       #[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
#---
x = [1,2,3,4,5]
for i,s in enumerate(x):
    print ('x[{}] = {}'.format(str(i),str(s)))
#x[0] = 1
#x[1] = 2
#x[2] = 3
#x[3] = 4
#x[4] = 5
#---
x = [1,2,3,4,5]
y = [10,20,30,40,50]
for i,j in zip(x,y):
    print ('x=[{}], y=[{}]'.format(i,j))
#x=[1], y=[10]
#x=[2], y=[20]
#x=[3], y=[30]
#x=[4], y=[40]
#x=[5], y=[50]

#■■■■■■■■■■
#■リスト作成
x = [0]*5                       #[0, 0, 0, 0, 0]
x = [1,2]*5                     #[1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
x = range(5)                    #[1, 2, 3, 4, 5]
[i * 2 for i in range(5)]       #[1,2,3,4,5] -> [2, 4, 6, 8, 10]
[s.upper() for s in x]          #['ABC', 'def'] -> ['ABC', 'DEF']
x = [(x,y) for x in range(3) for y in range(x,3)]
                                #[(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]

#■■■■■■■■■■
#■print
print('Hello world')
print('Hello world', x)
print('Hello world', end='')
print('x={}, y={}'.format(x, y))
print(f'{x:05}') #x=255 -> 00255
print(f'{x:b}')  #x=255 -> 11111111
print(f'{x:o}')  #x=255 -> 377
print(f'{x:x}')  #x=255 -> ff
print(f'{x:X}')  #x=255 -> FF
print('a','b','c', sep=(',')) #a,b,c
print ('{:.5f}'.format(0.123456789))	#0.12346

#■■■■■■■■■■
#■数値系関数
len('hello')	#5
str(30)			#'30'
str(-3.14)		#'-3.14'
int(7.7)		#7

#■■■■■■■■■■
#■文字系関数
x = 'Hello world'
'Hello world'.upper()           #'HELLO WORLD'
'Hello world'.lower()           #'hello world'
'H'.isupper()                   #True
'h'.islower()                   #True
'A'.isalpha()                   #True
'1'.isalnum()                   #True  a-z,A-Z,0-9
'1'.isdecimal()                 #True 0-9
' '.isspace()                   #True
'Gundam'.istitle()              #True  単語の先頭だけが大文字か?
'Gundam'.startswith('Gu')       #True
'Gundam'.endswith('am')         #True
' '.join(['Hello', 'world'])    #'Hello world'
'***'.join(['Hello', 'world'])  #'Hello***world'
'Hello world'.split()           #['Hello', 'world']
'Hello world'.split('o')        #['Hell', ' w', 'rld']
'Hello'.rjust(10,'*')           #'*****Hello'
'Hello'.ljust(10,'*')           #'Hello*****'
'Hello'.center(10,'*')          #'**Hello***'
'   Hello   '.strip()           #'Hello'
'   Hello   '.lstrip()          #'Hello   '
'   Hello   '.rstrip()          #'   Hello'
'Hello world'.replace('o','0')  #'Hell0 w0rld'

#■■■■■■■■■■
#■乱数
import random
x = random.random()                # 0.000 < x < 1.000
x = random.uniform(0, 3)           # 0.000 < x < 3.000
x = random.randint(0, 3)           # 0 <= x <= 3
x = randrange(0, 10, 3)            # 0,3,6,9
x = random.sample('abcdefg',3)     #['a', 'c', 'b']  重複なし
x = random.choices('abcdefg',k=3)  #['g', 'g', 'a']  重複可能性あり
#---
x = [1,2,3]
random.shuffle(x)

#■■■■■■■■■■
#■判定
if x == 1:
else x == 0:
else:

#■■■■■■■■■■
#■while ループ
x = 0
while x < 5:
    x = x + 1
    if x == 2:
        continue    #Skip
    print (str(x))
    if x == 3:
        break       #Exit
#-> 1\n3

#■■■■■■■■■■
#■range()
range(0,10, 2)     #0,2,4,6,8  // Start, stop, step
range(5, -1, -1)   #5,4,3,2,1,0

#■■■■■■■■■■
#■for ループ
for i in range(10):
    if i == 5:
        break
else:
    print('breakしなかった時だけ')

#■■■■■■■■■■
#■関数/function
def func(x):
    print(str(x))
    return x+1

#■■■■■■■■■■
#■スコープ / Global
x = 1
y = 1
def func():
    global x  #<-
    x = 2
    y = 2
func() #x=2,y=1

#■■■■■■■■■■
#■例外処理
try:
    x = 42 / 0
except ZeroDivisionError as e:
    print('エラーの時だけ: Invalid argument: {}'.format(e))
finally:
    print('必ず処理される')

#■■■■■■■■■■
#■ソート
x = [5, 1, 3, 2, 4]
x.sort()	#[1, 2, 3, 4, 5]
#---
x = ['ア', 'あ', 'A', 'a', 'A']
x.sort()                #['A', 'a', 'あ', 'ア', 'A']
x.sort(reverse=True)    #['A', 'ア', 'あ', 'a', 'A']
#reverse=True           #keyと同時指定可能
#key=str.lower          #大文字小文字無視
#key=len                #長さ順
sorted(x)               #[5, 1, 3, 2, 4]  #コピーしてソート

#■■■■■■■■■■
#■辞書
Gundam = {'size': 18.0, 'code': 'RX-78-2', 'pilots': 'Amuro Ray'}
for v in Gundam.values():
    print (v)       #18.0 / RX-78-2 / Amuro Ray
#---
for k in Gundam.keys():
    print (k)       #size / code / pilots
#---
for i in Gundam.items():
    print (i)       #('size', 18.0) / ('code', 'RX-78-2') / ('pilots', 'Amuro Ray')
    print (i[0])    #size / code / pilots
    print (i[1])    #18.0 / RX-78-2 / Amuro Ray
#---
'size' in Gundam.keys()     #True
'size' in Gundam.values()   #False
18.0 in Gundam.values()     #True
#---
Gundam.setdefault('color', 'white')    #未登録の場合追加される
Gundam.setdefault('size', 20.0)        #登録済の場合何も設定されず現在値を返す
#---
GundamA = {'size': 18.0, 'code': 'unknown'}
GundamB = {'code': 'RX-78-2', 'pilots': 'Amuro Ray'}
Gundam = {**GundamA , **GundamB}  #{'size': 18.0, 'code': 'RX-78-2', 'pilots': 'Amuro Ray'}

#■■■■■■■■■■
#■正規表現
#\d	任意の数字
#\D	任意の数字以外
#\s	任意の空白文字
#\S	任意の空白文字以外
#\w	任意の英数字
#\W	任意の英数字以外
#\A	文字列の先頭
#\Z	文字列の末尾
#.	任意の一文字
#*	0回以上の繰り返し
#+	1回以上の繰り返し
#?	0回または1回
#{n}		n回の繰り返し
#{n1,n2}	n1-n2回の繰り返し
#[]	集合
#|	和集合
#()	グループ化
import re
s = 'Hello world 123!'
regex = re.compile(r'wo')
regex.search(s)		#<re.Match object; span=(6, 8), match='wo'>
regex = re.compile(r'\d\d\d')
regex.search(s)		#<re.Match object; span=(12, 15), match='123'>

#■■■■■■■■■■
#■クラス / class
class Sample:
    def __init__(self):
        print('constructor')
    def __del__(self):
        print('destructor')
    def func(self, x):
        return x+x

s = Sample()	#constructor
s.func(100)		#200
s = ''			#destructor   s = 0 , s = 'abc'


#■■■■■■■■■■
#■マルチプロセス
from multiprocessing import Process
from time import sleep
def func_1(num):
    print ('func1---')
    sleep(num)
    print ('---func1')

def func_2(num):
    print ('func2---')
    sleep(num)
    print ('---func2')

if __name__ == '__main__':
    p = Process(target=func_1, args=(10,))
    p.start()
#   p.join()  #func_1終了まで待機
#   p.join(5) #finc_1起動後5秒待機
    func_2(10)

Blender x Python 三角形のドミノ倒し

一直線ばかりだったドミノを少し変化させていきたいと思います。倒すドミノを1枚づつ増やしていく配置としました。あまり面白味がないので、ドミノの配置に少し乱数を加え、マテリアルを2種類用意してランダムに割り当てました。
そろそろ立体にしてみようかと思っているところです。

#blender 3.0.0
import bpy
import random

for item in bpy.data.meshes:
    bpy.data.meshes.remove(item)

domino_distance = 10
domino_count = 50
domino_width = 4

mat_b = bpy.data.materials.new("Blue")
mat_b.diffuse_color = (0, 0, 1, 1.0)

mat_w = bpy.data.materials.new("White")
mat_w.diffuse_color = (1, 1, 1, 1.0)

for x in range(1,domino_count):
    for y in range(x):
        x_location = (x - 1)*domino_distance + random.random()
        y_location = (y-x/2)*domino_width *3
        bpy.ops.mesh.primitive_cube_add(location=(x_location, y_location, 0), scale=(1,domino_width,10),rotation=(0, 0, 0.0) )
        bpy.ops.rigidbody.object_add()
        bpy.context.object.rigid_body.friction = 1

        if random.random() > 0.6:
            bpy.context.object.data.materials.append(mat_w)
        else:
            bpy.context.object.data.materials.append(mat_b)

    if x== 1:
        bpy.context.object.rotation_euler[1] = 0.3

s=domino_distance * domino_count*1.2
bpy.ops.mesh.primitive_plane_add(size=s,  location=(s/2 - 10, 0, -10 ), scale=(1, 1, 1))
bpy.ops.rigidbody.object_add()
bpy.context.object.rigid_body.type = 'PASSIVE'

f:id:oneonet:20220316163040p:plain

Blender x Python アルキメデスの螺旋でドミノを並べるスクリプト

アルキメデスの螺旋(Archimedes' spiral)と呼ばれる方程式 r=a\theta によって表される曲線(うずまき)があります。線同士の間隔が等しいのが特徴です。この方程式を利用して渦巻でドミノを並べてみました。

グラフにプロットするだけなら計算式そのままで良いのですが、ドミノということでポイントが2つあります。1つは、等間隔となる必要があり、1つドミノ(キューブメッシュ)を配置したら一定距離になるまで次のドミノを配置しないこと。直前のドミノのx1,y1と配置しようとする新ドミノの位置x2,y2の距離を測り、距離が一定値以上となったら配置するようにしています。ドミノの高さ以下の距離であれば大丈夫です。 プロットする位置は計算で導き出せるようなのですが、何をしてもうまくいかなかったので、細かくループを回してその都度距離を測定する方法にしました。2点目はドミノの角度です。グラフにするだけなら角度という概念は出てきませんが、ドミノが前のドミノと次のドミノの中間の角度になる必要がありますので、プロットするタイミングでZ軸の角度を調整します。

動画を見て頂けるとわかるのですが、ドミノを並べる個数とその間隔、うずまきの密集具合も指定することができ、どの場合でもドミノがキレイに倒れます。

f:id:oneonet:20220316162743p:plain
2000個までやってみました。スクリプトの実行完了に10分ぐらいかかります。パラメタを少し変えるだけで、渦の密度を変化させられるのも面白いですね。

是非気持ちよく倒れていくドミノの様子を動画でご覧になってください。
oneonet-blog.blogspot.com

Blender ドミノアニメーションに自動で効果音を付けてみた

オブジェクトの衝突に合わせて効果音を設定するアドインを使って、ドミノ倒しに効果音を設定してみました。これはほんとにスゴイとしか言いようがないです。最後のドミノが倒れた後の「トコトコン♪」部分最高じゃないですか。

■前準備
ドミノの並べるスクリプトもしくは自分で並べてドミノ倒しを準備しましょう。ポイントはリジッドボディですね。面倒な方は以下をご利用ください。工夫としては、ドミノの隙間を少しづつ短くしている点です。これにより音の出る間隔が短くなるはずで、それに合わせて効果音が短い間隔で再生されることがお分かり頂けるかと思います。

#blender 3.0.0で動作確認
import bpy

for item in bpy.data.meshes:
    bpy.data.meshes.remove(item)

bpy.ops.mesh.primitive_plane_add(size=400,  align='WORLD', location=(0, 0, -5), scale=(1, 1, 1))
bpy.context.object.rotation_euler[2] = 0.78

bpy.ops.rigidbody.object_add()
bpy.context.object.rigid_body.type = 'PASSIVE'

for i in range (0 ,100 ):
    bpy.ops.mesh.primitive_cube_add(location=(-200+i*5-((i/10)**2) , 0, 0), scale=(1,4,10) )
    bpy.ops.rigidbody.object_add()
    bpy.context.object.rigid_body.friction = 1
    if i==0:
        bpy.context.object.rotation_euler[1] = 0.3

■効果音設定
例のアドインで効果音を設定します。ポイントは[しきい値]で、落下等の衝突より移動が少ないので、値は「0.01」ぐらいを目安にしてください。落下するより動きが少ないため、「0.1」だと衝突として検出せず効果音が設定されません。音源はドミノの倒れる音が良いとは思いますが、あえて違う音を設定してみました。今回は、WindowsPCにUSBデバイスを接続した時の音です。効果音次第で楽しめそうな気がします。
アドインにより効果音を設定した後の設定です。効果音の間隔が短くなっていることがおわかり頂けるかと思います。最後の「トコトコン♪」はこうなってます。
f:id:oneonet:20220316162508p:plain

是非ミラーサイトで動画ご覧になってください。面白いですよ。
oneonet-blog.blogspot.com

Blender:オブジェクトの衝突時に自動で効果音を付ける方法

リジッドボディを利用してオブジェクトの衝突、落下などなどやった時に丁度良いタイミングで音が出たらいいなと思いませんか?そんな希望を叶えるアドインの紹介。これスゴイですよ。
github.com

■セットアップ

  1. リンク先から Bake-Collisions-To-Sounds.py をダウンロード
  2. Blenderを起動し、[編集]→[プリファレンス]
  3. [アドオン]→[インストール]を選択し、先ほどダウンロードしたファイルを選択し、[アドオンをインストール]
  4. インストールしたアドオンを有効化しておく

f:id:oneonet:20220316161859p:plain

■利用方法
アニメーション再生するとオブジェクトが衝突するように事前設定。オブジェクトを配置し、リジッドボディを設定しておきます。その時、何フレームまでのアニメーションになるのかも確認しておきます。
アニメーションの準備ができたら以下を操作。

  1. アニメーションの対象となるオブジェクトを選択状態とする(ココ重要)
  2. [F3]を押して、検索窓に[キーフレーム]と入力。[キーフレームにベイク]を選択。※英語環境の場合、[Bake to Keyframes]
  3. 事前にアニメーションで確認した最終フレーム数に変更して[OK]。1フレーム毎にキーフレームが設定されます。
  4. 画面上部[オブジェクト]→[Bake Collisions To Sounds]でアドインを起動
  5. パラメタについては以下を参考に設定します。
  • [トラック]はまずは"1"に。
  • [ボリューム]は名前の通り。まずは10~30ぐらいの大き目が良いです。理由が後程。
  • しきい値は、小さいほど衝突と判定しやすくなり、音がたくさん挿入されます。0.1前後でオブジェクトに合う値を探してください。
  • フレーム範囲は事前に確認したアニメーションのフレームを設定します。
  • ファイルは、[ファイル名]カンマ[オフセット]の形で指定します。例えば、"c:\tmp\sound.mp3, +10"とか。オフセットは、衝突を検出したフレームからどれだけフレームをずらして音をセットするのかを指定します。最初は"+0"で良いです。オフセットの指定を忘れるとエラーになります。
  • 最後に[OK]を押せば効果音の挿入完了です。

アニメーションを再生させれば合わせて音が出ます。アニメーションレンダリングすれば音付きのアニメーションが出力されます。
f:id:oneonet:20220316162031p:plain
f:id:oneonet:20220316162040p:plain
まずはキーフレームにベイクするところから。忘れがち。

f:id:oneonet:20220316162053p:plain
ボリュームとしきい値はお好みで。ファイル名の後ろにオフセットを付けないとアドインがエラーとなります。

f:id:oneonet:20220316162106p:plain
[Video Editing]で挿入された効果音を確認できます。複数の効果音が密集して挿入されているので、枠が重なった状態で表示されています。

■TIPS

  • オブジェクトを編集した場合はキーフレームのベイクからやり直します。音付けだけやり直す場合は、Video Editing Windowから挿入された効果音を削除してアドインを再実行します。
  • 衝突判定の仕組みはオブジェクトの位置の変化を見ています。各フレームで、対象フレームと前2フレームからオブジェクトの位置を確認し、一定速度で移動していない場合、かつその変化が[しきい値]を超えた場合に効果音が追加されます。一定速度ではない場合に、X,Y,Z軸それぞれでチェックしており、音を変化させたい場合は、音とオフセットをカンマで続けて指定します。
  • また、位置の変化量を効果音量として設定しているので、移動変化速度=衝突速度が速いほど大きな音となります。アニメーションによっては再生音が小さく設定される可能性があるため、最初は大き目の音を設定して調整していくと良いです、と作者の方もコメントされています。