import os
import bpy
import math
import copy
import time
import pandas_datareader.data as data
from datetime import datetime,timedelta
#blender 2.91
#Mac Big Sur
#ターミナル起動
#/Applications/Blender.app/Contents/MacOS/Blender
#GLOBAL
# count
# xmove
# bairitsu
#日経平均株価データのクラスオブジェクト、カプセルデータ
class nikkeiClass:
def __init__(self, cindex, chigh,clow,copen,cclose,cadjclose):
self.index = cindex
self.high = chigh
self.low = clow
self.open = copen
self.close = cclose
self.adjclose = cadjclose
#https://bluebirdofoz.hatenablog.com/entry/2018/04/25/231414
# オブジェクトの原点変更
# オブジェクトの原点を指定位置に移動する
# 引数 arg_objectname:指定オブジェクト名
# 戻り値
def set_origin_cursor(arg_objectname='Default',arg_location=(0,0,0)):
# 他のオブジェクトの寸法を適用しないよう全てのオブジェクトを走査する
for ob in bpy.context.scene.objects:
# 非選択状態に設定する
#ob.select=False#2.7
ob.select_set(False) #2.9
# 指定オブジェクトを取得する
selectob = bpy.data.objects[arg_objectname]
# 変更オブジェクトをアクティブに変更する
#bpy.context.scene.objects.active = selectob#2.7
bpy.context.view_layer.objects.active=selectob#2.9
# 変更オブジェクトを選択状態にする
#selectob.select=True #2.7
selectob.select_set(True) #2.9
# 3Dカーソルの元の位置を記録しておく(参照型のコピー)
#cursorpos = copy.copy(bpy.context.scene.cursor_location)
# 3Dカーソルの位置を指定位置に移動する
#bpy.context.scene.cursor_location = arg_location#2.7
bpy.context.scene.cursor.location=arg_location#2.9
# オブジェクトの原点を3Dカーソル位置に移動する
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
# 3Dカーソルの位置を元に戻す
#bpy.context.scene.cursor_location = cursorpos #2.7
#bpy.context.scene.cursor.location=cursorpos#2.9 not use
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
return
def fcandle(candleType,kopen,kclose,bairitsu,xmove,mat1,mat2):
#count is Global
global count
#global xmove
#global bairitsu
if bairitsu=='' or bairitsu==0:
print('def fcandle(),bairitsu not defined,use 0.001')
bairitsu=0.001
if count=='':
print('def fcandle(),count is Global, not defined, Please check, so use 1')
count=1
candle=candleType
#1.円柱location:図形の中心座標 radius:円の半径 depth:高さ rotation:立体の回転角(rad)
bpy.ops.mesh.primitive_cylinder_add(location=(0, 0, 0), radius=0.022, depth=3, rotation=(0, 0, 0))
if candle=='yousen':
bpy.context.object.data.materials.append(mat1) # 材質(赤)指定
else:#insen
bpy.context.object.data.materials.append(mat2) # 材質(青)指定
bpy.ops.transform.translate(value=(xmove,0,0))
#location=bpy.context.object.location
for obj in bpy.context.selected_objects:
if obj.name=='Cylinder':
tmpcount=count+1
obj.name = "candle"+str(tmpcount)
#bpy.ops.object.shade_smooth()
new_bar = bpy.context.object
for vert in new_bar.data.vertices:
if candle=='yousen':
if vert.co[2] > 0:#Zjiku ni up
vert.co[2] = kclose*bairitsu#close
else:
vert.co[2] = kopen*bairitsu
elif candle=='insen':
if vert.co[2] > 0:
vert.co[2] = kopen*bairitsu
else:
vert.co[2] = kclose*bairitsu
clocation=(xmove,0,(kopen+kclose)*bairitsu/2)
set_origin_cursor(obj.name,clocation)
location=bpy.context.object.location
return location#candle
#end def
def fcandle_hige(candleType,khigh,klow,bairitsu,candleLocation,mat1,mat2):
global count
#global xmove
#global bairitsu
#count,bairitsu is Global
#if bairistu=='' or bairitsu==0:
# print('def fcandle_hige(),bairitsu not defined, so use 0.001')
# bairitsu=0.001
if count=='':
print('def fcandle_hige(),count is Global, not defined, Please check, so use 1')
count=1
candle=candleType
bpy.ops.mesh.primitive_cylinder_add(location=(0, 0, 0), radius=0.022/7, depth=3, rotation=(0, 0, 0))
if candle=='yousen':
bpy.context.object.data.materials.append(mat1) # 材質(赤)指定
else:#insen
bpy.context.object.data.materials.append(mat2) # 材質(青)指定
bpy.ops.transform.translate(value=(candleLocation[0],candleLocation[1],0))#hige location is same candle location
for obj in bpy.context.selected_objects:
if obj.name=='Cylinder':
tmpcount=count+1
obj.name = "hige"+str(tmpcount)
#xmove+=0.048
#bpy.ops.object.shade_smooth()
new_bar = bpy.context.object
for vert in new_bar.data.vertices:
if vert.co[2] > 0:#Zjiku ni up
vert.co[2] = khigh*bairitsu#close
else:
vert.co[2] = klow*bairitsu
set_origin_cursor(obj.name,candleLocation)
#end def
# テキストオブジェクトの追加
def add_text(text, t_color,location):
# 角度設定時に利用
ROT_QUATER = math.pi / 2
bpy.ops.object.text_add()
ob = bpy.context.object
ob.data.body = text
ob.data.extrude = 0.05
ob.data.bevel_depth = 0.002
bpy.ops.font.open(filepath="/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc")
bpy.data.fonts["HiraginoSans-W6"].name = "HiraginoSans-W6"
fnt = bpy.data.fonts.load('/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc')
ob.data.font = fnt
ob.rotation_euler[0] = ROT_QUATER
# 3Dカーソルの位置を0,0,0
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
#3Dカーソルを原点を変更してから、translateする必要がある。
bpy.ops.transform.translate(value=location)#26500*0.001,(1.8,0.1/3,26.5)
bpy.ops.transform.resize(value=(0.2,0.1,0.2)) # 図形を変形
#ob.rotation_euler[2] = ROT_QUATER
ob.data.align_x = 'CENTER'
ob.data.align_y = 'CENTER'
mat = bpy.data.materials.new('color_txt')
mat.diffuse_color = t_color#(0.8, 0.8, 0.8, 1.0)
# マテリアルスロットを追加する
bpy.ops.object.material_slot_add()
# 作成したマテリアルスロットに新規マテリアルを設定する
bpy.context.object.active_material = mat
#ob.active_material.diffuse_color = (1,0,0)
#bpy.context.object.active_material.diffuse_color = (1,0,0)
#mat.diffuse_color = (1.0,0.0,0.0)
#mat.diffuse_color = (1,0,0)#t_color #error , I do not yet 2020/05/15
#ob.data.materials.append(mat)
#ob.active_material.diffuse_color = t_color # error
#convert to mesh
#s = bpy.context.scene.objects.active
#s.name = "text_name"
bpy.ops.object.convert(target='MESH')# work
for obj in bpy.context.selected_objects:
if obj.name=='Text':
obj.name=text
return ob
#end def
def add_textKai(text, t_color,location,size):
# 角度設定時に利用
ROT_QUATER = math.pi / 2
bpy.ops.object.text_add()
ob = bpy.context.object
ob.data.body = text
ob.data.extrude = 0.05
ob.data.bevel_depth = 0.002
bpy.ops.font.open(filepath="/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc")
bpy.data.fonts["HiraginoSans-W6"].name = "HiraginoSans-W6"
fnt = bpy.data.fonts.load('/System/Library/Fonts/ヒラギノ角ゴシック W6.ttc')
ob.data.font = fnt
ob.rotation_euler[0] = ROT_QUATER
# 3Dカーソルの位置を0,0,0
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
#3Dカーソルを原点を変更してから、translateする必要がある。
bpy.ops.transform.translate(value=location)#26500*0.001,(1.8,0.1/3,26.5)
bpy.ops.transform.resize(value=size) # 図形を変形(0.2,0.1,0.2)
#ob.rotation_euler[2] = ROT_QUATER
ob.data.align_x = 'CENTER'
ob.data.align_y = 'CENTER'
mat = bpy.data.materials.new('color_txt')
mat.diffuse_color = t_color#(0.8, 0.8, 0.8, 1.0)
# マテリアルスロットを追加する
bpy.ops.object.material_slot_add()
# 作成したマテリアルスロットに新規マテリアルを設定する
bpy.context.object.active_material = mat
#ob.active_material.diffuse_color = (1,0,0)
#bpy.context.object.active_material.diffuse_color = (1,0,0)
#mat.diffuse_color = (1.0,0.0,0.0)
#mat.diffuse_color = (1,0,0)#t_color #error , I do not yet 2020/05/15
#ob.data.materials.append(mat)
#ob.active_material.diffuse_color = t_color # error
#convert to mesh
#s = bpy.context.scene.objects.active
#s.name = "text_name"
bpy.ops.object.convert(target='MESH')# work
for obj in bpy.context.selected_objects:
if obj.name=='Text':
obj.name=text
return ob
#end def
#2020/12/29
#print('Max:',max(df['High']))#最大値が取れる
#高値の最大値maxと安値の最小値minを取得して
#500円ごとのバー基準株価線(26500,27000,27500)を自動で作成したい。
def makeBarMaxtoMin(highobj,lowobj):
#print('Max:',max(highobj))#df['High']
#print('Min:',min(lowobj))#df['Low']
#bairitsu is Global
global count
global xmove
global bairitsu
#if bairistu=='' or bairitsu==0:
# print('def makeBarMaxtoMin(),bairitsu not defined,use 0.001')
# bairitsu=0.001
#every 500yen
priceRange=500
#highPriceNumはバーの本数を取得する
highPriceNum,highmod=divmod(max(highobj),priceRange)#26200/priceRange
lowPriceNum,lowmod=divmod(min(lowobj),priceRange)
#高値については、余りが出たら、1を追加する
#print('highmod:',highmod)
if highmod!=0:
highPriceNum=highPriceNum+1
#Rangeで範囲を指定してしようする場合1加算では足りないので、rangeで更に1を加算する
#print('after highPriceNum:',highPriceNum)
for i in range(math.floor(lowPriceNum),math.floor(highPriceNum+1)):
#range()が出力するのは、(highPriceNum-1)なので1を加算してhighPriceNum分までを計算させる
print('barPrice:',i*priceRange)
#i*priceRangeの位置でBarオブジェクトを作成する。
# 3Dカーソルの位置を0,0,0
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
color=(0.8,0.5,0.8,0.8)
add_text(str(i*priceRange), color,(1.8,0.1/3, (i*priceRange)*bairitsu+0.045))#0.045 is ajustment
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
# 平板作成 26500bar1
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0), size=2.0)
bpy.ops.transform.translate(value=(0.0,0.1/2,(i*priceRange)*bairitsu))#26500*0.001
bpy.ops.transform.resize(value=(2.0,0.06/6,0.01/6)) # 図形を変形
bpy.context.object.data.materials.append(mat3) # 材質
for obj in bpy.context.selected_objects:
if obj.name=='Cube':
obj.name='bar_'+str(i*priceRange)
#end def
######################################################################
#main
######################################################################
count=0#count is Global
xmove=0.8#1# move distancs
bairitsu=0.001# size Global
print('this Code for reading CSV.')
print('>>>>>CSV出力>>>>>')
#ここからCSV出力用
checkLineCount=0
df=[]
#CSVを読み込む
ifile='/Users/toshiromaseda/Documents/blender/script/2021年1月14日日経.csv'
try:#ファイルが存在しないときのエラー処理try
with open(ifile,'tr') as fin:
for iline in fin:
try:
tmp=iline.strip().split(",")#stripしてからsplit()だと理解している。
if 'High' in tmp[0]:
continue
if len(tmp) < 6:#全部で6列存在する。
print('ERROR 列の数があっていません。少ないようです。行数:',checkLineCount)
break
print('iline[]:',tmp[0])
#ここから処理を入れていけばよい
df.append(nikkeiClass(tmp[0],tmp[1],tmp[2],tmp[3],tmp[4],tmp[5]))
checkLineCount+=1
except:
print ('error')
except FileNotFoundError as e: # FileNotFoundErrorは例外クラス名
print("ファイルが見つかりません。パス、ファイル名を確認してください", e)
ifile.close()
sys._exit()#ファイルがなければ終了
#Stock date
n=maenohi=5
end = datetime.now()
start = end - timedelta(days=n)
#start = end - timedelta(days=maenohi)#end.day-7 changes
#start = datetime(end.year, end.month, end.day-7)
##土日があると表示させるローソク足の数が少ないので、数が満たされるまでループするためSleepを入れた
#大量のデータはCSVで取得してからローソク足にしたほうが良さそう
'''
x=0
while x<=(maenohi):
start = end - timedelta(days=n)
df=data.DataReader('^N225','yahoo',start,end)#3990.T,#^N225
date=df.index# 日付
time.sleep(3)
print('len(date):',len(date))
print('3秒待ち')
x=len(date)
n=n+1
print(df.head(8))
if len(date)==0:
print('ERROR Can not get any Price data, Check internet or yahoo finance com.')
sys.exit()
'''
# 既存要素削除
for item in bpy.data.meshes:
bpy.data.meshes.remove(item)
for item in bpy.data.materials:
bpy.data.materials.remove(item)
#bpy.ops.outliner.item_rename()
#2.立方体 location:図形の中心座標 size:立方体の一辺の長さ rotation:立体の回転角(rad)
#bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0), size=1.0, rotation=(0, 0, 0))
# 2.材質の定義(赤色)
mat1 = bpy.data.materials.new('Red')
mat1.diffuse_color = (1.0, 0.0, 0.0, 1.0)
# 3.材質の定義(青色)
mat2 = bpy.data.materials.new('blue')
mat2.diffuse_color = (0.0, 0.0, 1.0, 1.0)
mat3 = bpy.data.materials.new('gray')
mat3.diffuse_color = (0.8, 0.8, 0.8, 0.5)
mat5 = bpy.data.materials.new('yellow')
mat5.diffuse_color = (1.0, 0.9, 0.1, 1.0)
# 3Dカーソルの位置を0,0,0
#bpy.context.scene.cursor_location =(0.0,0.0,0.0)
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
#date=df.indexをカウントして、それぞれを取得する。ちょっとスマートではないが、、
for fdate in df:
khigh=float(fdate.high)
klow=float(fdate.low)
kopen=float(fdate.open)
kclose=float(fdate.adjclose)
#ここに日付ごとの処理を入れる。
#
if kopen-kclose<0:
candle='yousen'#color Red
#print('陽線')
else:
candle='insen'#color Blue
#print('陰線')
#objを生成する。
#始値、終値でZ軸の高さを設定する。
#print( fdate.strftime("%Y/%m/%d") )#2020-12-22 00:00:00となるので、時刻を除く2020-12-22
#Candle_Main####################################################
candleLocation=fcandle(candle,kopen,kclose,bairitsu,xmove,mat1,mat2)
xmove+=0.048
#Candle_hige####################################################
fcandle_hige(candle,khigh,klow,bairitsu,candleLocation,mat1,mat2)
#count is Global
count+=1
#最後のローソクのobjの横に矢印を与える
#この場合、ローソクの原点を移動させて、ローソクの中心に移動させる必要がある
candleCount=0
for obj in bpy.data.objects:
if 'candle' in obj.name:
#print(obj.name)
candleCount+=1
candleName='candle'+str(candleCount)
#Arrow
bpy.ops.mesh.primitive_cone_add(radius1=1, radius2=0, depth=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(0.05, 0.05, 0.05))
#図形を回転(Y軸周りに90°)
bpy.ops.transform.rotate(value=-3.1415/2 ,orient_axis='Y')
bpy.context.object.data.materials.append(mat5)
bpy.ops.transform.translate(value=(bpy.data.objects[candleName].location[0]+0.08,bpy.data.objects[candleName].location[1],bpy.data.objects[candleName].location[2]))#
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
#open,close
color=(1.0,1.0,1.0,1.0)
#str,color,location,size
lastOpen=float(df[len(df)-1].open)#始値
lastClose=float(df[len(df)-1].close)#終値
roundOpen='O:'+str(round(lastOpen,2))#画面に表示させる直近の始値文字
roundClose='C:'+str(round(lastClose,2))#画面に表示させる直近の終値文字
add_textKai(roundOpen, color,(xmove+0.1,0,lastOpen*bairitsu),(0.06,0.05,0.06))
add_textKai(roundClose, color,(xmove+0.1,0,lastClose*bairitsu),(0.06,0.05,0.06))
#end.strftime("%Y/%m/%d") or date[len(date)-1].strftime("%Y/%m/%d")today
color=(0.0,1.0,0.0,1.0)
coneLocation=bpy.data.objects['Cone'].location
#tyokkinDay=date[len(date)-1].strftime("%Y/%m/%d")
tyokkinDay=df[len(df)-1].index
if len(df)>0:
add_textKai(tyokkinDay,color, (coneLocation[0]+0.16,coneLocation[1],coneLocation[2]),(0.06,0.05,0.06))
#print('last day:',date[len(date)-1])
#始値、終値、直近の日付の調整。オブジェクトが重なってしまう金額が近いときに離す処理
if candle=='yousen':#Openが下側でCloseが上側にあるはず
#bpy.data.objects[roundOpen].dimensions[1]#Z方向の幅サイズではなくてY軸、理由はローカルで回転させたから
#bpy.data.objects[roundClose].dimensions[1]
#bpy.data.objects[tyokkinDay].dimensions[1]
#始値、終値、直近の日付のlocationを取得する。Z方向ではなくてY軸の幅の半分を上下に加減算して重なりを調べる
#bpy.data.objects[roundOpen].location[2]
#bpy.data.objects[roundClose].location[2]
#bpy.data.objects[tyokkinDay].location[2]
#roundCloseを調整する.roundCloseの下側位置が、直近日付の上側位置以下になっているときに、直近日付の上の方にroundCloseを上へ移動させる
#location is [2]:Z, dimensions is [1]:Y
if bpy.data.objects[roundClose].location[2]-bpy.data.objects[roundClose].dimensions[1]/2 <= bpy.data.objects[tyokkinDay].location[2]+bpy.data.objects[tyokkinDay].dimensions[1]/2:
#直近日付の位置に、プラスへ移動
bpy.data.objects[roundClose].location[2]=bpy.data.objects[tyokkinDay].location[2]+bpy.data.objects[tyokkinDay].dimensions[1]*1.5
#roundOpenを調整する。roundOpenの「上側」位置が、直近日付の「下側」以上になっているときに、直近日付の「下の方」にroundOpenを下へ移動させる
if bpy.data.objects[roundOpen].location[2]+bpy.data.objects[roundOpen].dimensions[1]/2 >=bpy.data.objects[tyokkinDay].location[2]-bpy.data.objects[tyokkinDay].dimensions[1]/2:
#「マイナス」へ移動
bpy.data.objects[roundOpen].location[2]=bpy.data.objects[tyokkinDay].location[1]-bpy.data.objects[tyokkinDay].dimensions[1]*1.5
else:#insen Openが上側でCloseが下側にあるはず.Yousenとは逆
#roundOpen
print('insen')
if bpy.data.objects[roundOpen].location[2]-bpy.data.objects[roundOpen].dimensions[1]/2<=bpy.data.objects[tyokkinDay].location[2]+bpy.data.objects[tyokkinDay].dimensions[1]/2:
bpy.data.objects[roundOpen].location[2]=bpy.data.objects[tyokkinDay].location[2]+bpy.data.objects[tyokkinDay].dimensions[1]*1.5
print('Open')
#roundClose
if bpy.data.objects[roundClose].location[2]+bpy.data.objects[roundClose].dimensions[1]/2>=bpy.data.objects[tyokkinDay].location[2]-bpy.data.objects[tyokkinDay].dimensions[1]/2:
bpy.data.objects[roundClose].location[2]=bpy.data.objects[tyokkinDay].location[2]-bpy.data.objects[tyokkinDay].dimensions[1]*1.5
print('Close')
#2020/12/29
#print('Max:',max(df['High']))#最大値が取れる
#高値の最大値maxと安値の最小値minを取得して
#500円ごとのバー基準株価線(26500,27000,27500)を自動で作成したい。
#面倒だけど、別途高値、安値の配列オブジェクトを作って、メソッドに渡す
dfHigh=[]
dfLow=[]
for m in df:
dfHigh.append(float(m.high))
dfLow.append(float(m.low))
makeBarMaxtoMin(dfHigh,dfLow)
#camera move, near Cone obj
camera_obj = bpy.data.objects['Camera']
print(camera_obj.location)
camera_obj.location=(coneLocation[0]+0.16,coneLocation[1]-3.0,coneLocation[2])
#Neko呼び出し
file_path = '/Users/toshiromaseda/Documents/blender/model/テスト、研究モデル/Neko/Neko_simple_001.blend'
inner_path = 'Object'
object_name = 'Neko'
bpy.ops.wm.append(
filepath=os.path.join(file_path, inner_path, object_name),
directory=os.path.join(file_path, inner_path),
filename=object_name
)
bpy.context.scene.cursor.location=(0.0,0.0,0.0)
# 指定オブジェクトを取得する
#arg_objectname='Neko'
selectob = bpy.data.objects[object_name]
selectob.select_set(True) #2.9
selectob.location=(coneLocation[0],coneLocation[1]-0.1,coneLocation[2])
bpy.ops.transform.resize(value=(0.07,0.07,0.07)) # 図形を変形
print('done.')
''' Result
'''