non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

BlenderでPython使って最速降下曲線を描いてみる

背景

多摩六都科学館にいって、ボールを転がし、どの斜面が一番ボールが早く落ちるかを調べる実験する展示があり、 これの説明にサイクロイド曲線が使われている旨の説明があり、最速降下曲線と呼ばれるものらしい。

曲線の数式がわかったので、曲面を算出してそれをBlenderの物理シミュレーションでやってみようと思った。

そんな訳で、GWは家族で、科学館や、自然博物館めぐりをしていたkjunichiです。

Blenderで最速降下曲面をつくる

曲線をプロットする

最終的には立体化する必要があるが、これは押し出し機能を使えば出来そうと思い、 2次元の平面に最速降下曲線をプロットすることにした。

x = t - sin(t)
y = 1 + cos(t)

Blenderでの座標はWikiPediaとはy座標系が上下逆なので、1-cos(t)をこれに合わせて、変形している。

PythonにはC風のfor文がない件

算数が苦手なので、これにハマった。 ステップ数を指定して、1,2,3と整数を生成させ、これを元にプロットしたい変数tの最小値から最大値を取るように 計算させる必要があった。

tmin = 0.0
tmax = 3.1
step = 25
for i in range(0, step):
  t = i*((tmax - tmin)/(step - 1)) + tmin

こんな感じで、何とか、意図した値のtを得ることが出来た。

これを元に最速降下曲線プロットするコードが以下。

import bpy
import math

meshName = 'CurveMeshByData'
objName = 'CurveObjectByData'
mesh = bpy.data.meshes.new(meshName)
obj = bpy.data.objects.new(objName, mesh)
bpy.context.scene.objects.link(obj)

tmin = 0
tmax = 3.1
step = 25

vertices=[]
for i in range(0,step):
        t = i*((tmax-tmin)/(step-1))+tmin
        vertex = (t-math.sin(t), 1+math.cos(t), 0.0)
        vertices.append(vertex)

mesh.from_pydata(vertices, [], [])
mesh.update()

Blenderの画面は以下。

f:id:kjw_junichi:20160510071934p:plain:w800

立体化の準備

曲線をプロットできたので、これを押し出して立体化できるように、面を構成出来るようにプロットする頂点を追加する。

import bpy
import math

meshName = 'CurveMeshByData'
objName = 'CurveObjectByData'
mesh = bpy.data.meshes.new(meshName)
obj = bpy.data.objects.new(objName, mesh)
bpy.context.scene.objects.link(obj)

tmin = 0
tmax = 3.1
step = 25

vertices=[]
for i in range(0,step):
    for y in range(0,2):
        t = i*((tmax-tmin)/(step-1))+tmin
        vertex = (t-math.sin(t), (1+math.cos(t))*y, 0.0)
        vertices.append(vertex)

mesh.from_pydata(vertices, [], [])
mesh.update()

f:id:kjw_junichi:20160510071938p:plain:w800

面の作成

import bpy
import math

meshName = 'CurveMeshByData'
objName = 'CurveObjectByData'
mesh = bpy.data.meshes.new(meshName)
obj = bpy.data.objects.new(objName, mesh)
bpy.context.scene.objects.link(obj)

tmin = 0
tmax = 3.1
step = 25

vertices=[]
for i in range(0,step):
    for y in range(0,2):
        t = i*((tmax-tmin)/(step-1))+tmin
        vertex = (t-math.sin(t), (1+math.cos(t))*y, 0.0)
        vertices.append(vertex)

faces=[]

for i in range(0,step-1):
    idx=i*2
    face = (idx, idx+1, idx+3, idx+2)
    faces.append(face)
    
mesh.from_pydata(vertices, [], faces)
mesh.update()

f:id:kjw_junichi:20160510071941p:plain:w800

2次元図形を立体化する

描画した2次元図形を選択して編集モードに切り替え、bpy.ops.mesh.extrude_region_moveを実行することで立体化出来る。 (直前に描画した2次元図形は選択状態ではないので、ここで選択する必要があった。)

import bpy
import math

meshName = 'CurveMeshByData'
objName = 'CurveObjectByData'
mesh = bpy.data.meshes.new(meshName)
obj = bpy.data.objects.new(objName, mesh)
bpy.context.scene.objects.link(obj)

tmin = 0
tmax = 3.1
step = 25

vertices=[]
for i in range(0,step):
    for y in range(0,2):
        t = i*((tmax-tmin)/(step-1))+tmin
        vertex = (t-math.sin(t), (1+math.cos(t))*y, 0.0)
        vertices.append(vertex)

faces=[]

for i in range(0,step-1):
    idx=i*2
    face = (idx, idx+1, idx+3, idx+2)
    faces.append(face)
    
mesh.from_pydata(vertices, [], faces)
mesh.update()

bpy.context.scene.objects.active = bpy.data.objects[objName]
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={"mirror":False}, TRANSFORM_OT_translate={"value":(0, 0, -3), "constraint_axis":(False, False, True), "constraint_orientation":'NORMAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False})

f:id:kjw_junichi:20160510071930p:plain:w800

試しにボールを転がしてみた

最速降下曲線に斜面、円弧を並べて、Blenderで剛体シミュレーションしてみた。

真ん中が今回作成した最速降下曲線の斜面。手前は円弧の一部を使った斜面。

youtu.be

関連記事

5年前の記事