DEMの勉強2

意味も分からず線とか追加。厳密な当たり判定すらできず、ズルしてみたけど、うまく動いとる。ゲームとかならまだしも、解析でやっちゃいかんでしょー。

# -*- coding: utf-8 -*-

import sys
import math
import random
from PySide import QtGui,QtCore

g = 9.80665 # 重力加速度
rho = 10 # 粒子間密度
pn = 0 #粒子数

class Particle():
    def __init__(self,x,y,vx=0,vy=0):
        global pn
        self.n = pn #粒子NO.
        pn += 1
        self.r = 5 #半径
        self.x = x #X座標
        self.y = y #Y座標
        self.a = 0  #角度
        self.dx = 0 #X方向増加量
        self.dy = 0 #Y方向増加量
        self.da = 0 #角度増加量
        self.vx = vx #X方向速度
        self.vy = vy #Y方向速度
        self.va = 0 #角速度
        self.fy = 0
        self.fx = 0
        self.fm = 0
    
        self.m = 4.0/3.0*math.pi*rho*self.r**3 # 質量
        self.Ir = math.pi*rho*self.r**4/2.0 #慣性モーメント
        
        self.en = [] #弾性力(直方向)
        self.es = [] #弾性力(せん断方向)

    def config(self):
        self.en = [0 for i in range(pn)]
        self.es = [0 for i in range(pn)]
        
    def nextStep(self,dt):
        #位置更新(オイラー差分)
        ax = self.fx/self.m
        ay = self.fy/self.m
        aa = self.fm/self.Ir
        self.vx += ax*dt
        self.vy += ay*dt
        self.va += aa*dt
        self.dx = self.vx*dt
        self.dy = self.vy*dt
        self.da = self.va*dt
        self.x += self.dx
        self.y += self.dy
        self.a += self.da

class Line():
    def __init__(self,x1,y1,x2,y2):
        global pn
        self.n = pn #粒子NO.
        pn += 1
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        

class Interface():
    def __init__(self):
        pass


class DEM():
    
    def __init__(self):
        
        self.step = 0
        self.dt = 0.001
        self.pars = []
        self.lines = []
        
        # set particle
        for i in range(10):
            p = Particle(random.randint(-140,140),random.randint(0,90),
                         random.randint(-40,40),random.randint(-40,40))
            self.pars.append(p)
            
        p1 = Particle(120,80,0,0)
        p2 = Particle(-40,50,0,0)
        #p3 = Particle(10,20,-20,30)
        #p4 = Particle(-80,80,40,5)
        #self.pars.append(p1)
        self.pars.append(p2)
        #self.pars.append(p3)
        #self.pars.append(p4)
            
        # set line
        l1 = Line(-150,-100, 150,-100)
        l2 = Line(-150, 100,-150,-100)
        l3 = Line( 150, 100, 150,-100)
        l4 = Line(-150, 100, 150, 100)
        l5 = Line(-120,-10,80,-40)
        l6 = Line(100,50,-30,30)
        
        self.lines.append(l1)
        self.lines.append(l2)
        self.lines.append(l3)
        self.lines.append(l4)
        self.lines.append(l5)
        self.lines.append(l6)
        
        for p in self.pars:
            p.config()
 
    def calcForce(self):
        #2粒子間の接触判定
        combs = [(p1, p2) for p1 in self.pars for p2 in self.pars]
        for p1,p2 in combs:
            if p1.n == p2.n:
                continue
            
            lx = p1.x - p2.x
            ly = p1.y - p2.y
            ld = math.sqrt(lx**2+ly**2)
            
            if (p1.r+p2.r)>ld:
                cos_a = lx/ld
                sin_a = ly/ld
                self.force2par(p1,p2,cos_a,sin_a)
            else:
                p1.en[p2.n] = 0.0
                p1.es[p2.n] = 0.0
        
        #粒子と線の接触判定        
        combs = [(l, p) for l in self.lines for p in self.pars]
        for l,p in combs:
            hit = False
            th0 = math.atan2(l.y2-l.y1,l.x2-l.x1)
            th1 = math.atan2(p.y-l.y1,p.x-l.x1)
            a = math.sqrt((p.x-l.x1)**2+(p.y-l.y1)**2)
            d = abs(a*math.sin(th1-th0))
            if d < p.r:
                b = math.sqrt((p.x-l.x2)**2+(p.y-l.y2)**2)
                s = math.sqrt((l.x2-l.x1)**2+(l.y2-l.y1)**2)
                if a < s and b < s:
                    s1 = math.sqrt(a**2-d**2)
                    x = l.x1 + s1*math.cos(th0)
                    y = l.y1 + s1*math.sin(th0)
                    hit = True
                elif a < b and a < p.r:
                    x = l.x1
                    y = l.y1
                    hit = True
                elif b < p.r:
                    x = l.x2
                    y = l.y2
                    hit = True       
            if hit:
                self.force2line(p,l,x,y)
            else:
                p.en[l.n] = 0.0
                p.es[l.n] = 0.0
                pass
        
        #外力
        for p in self.pars:
            p.fy += -g*p.m #重力
    
    def force2line(self,p1,line,x,y):
        kn = 10**7 #弾性係数(法線方向)
        etan = 0 #粘性係数(法線方向)
        ks = 50000 #弾性係数(せん断方向)
        etas = 1000 #粘性係数(せん断方向)
        frc = 10 #摩擦係数
        
        lx = p1.x - x
        ly = p1.y - y
        ld = math.sqrt(lx**2+ly**2)
        cos_a = lx/ld
        sin_a = ly/ld
        
        #相対的変位増分
        un = +(p1.dx)*cos_a+(p1.dy)*sin_a
        us = -(p1.dx)*sin_a+(p1.dy)*cos_a+(p1.r*p1.da)
        #相対的速度増分
        vn = +(p1.vx)*cos_a+(p1.vy)*sin_a
        vs = -(p1.vx)*sin_a+(p1.vy)*cos_a+(p1.r*p1.va)
        #合力
        p1.en[line.n] += kn*un
        p1.es[line.n] += ks*us
        
        hn = p1.en[line.n] + etan*vn
        hs = p1.es[line.n] + etas*vs
        
        if hn <= 0.0:
            #法線力がなければ、せん断力は0
            hs = 0.0
        elif abs(hs)-frc*hn >= 0.0:
            #摩擦力以上のせん断力は働かない
            hs = frc*abs(hn)*hs/abs(hs)     
        
        #合力
        p1.fx += -hn*cos_a + hs*sin_a
        p1.fy += -hn*sin_a - hs*cos_a
        p1.fm -=  p1.r*hs
    
    def force2par(self,p1,p2,cos_a,sin_a):

        kn = 10**6 #弾性係数(法線方向)
        etan = 1000 #粘性係数(法線方向)
        ks = 5000 #弾性係数(せん断方向)
        etas = 1000 #粘性係数(せん断方向)
        frc = 10 #摩擦係数
        
        #相対的変位増分
        un = +(p1.dx-p2.dx)*cos_a+(p1.dy-p2.dy)*sin_a
        us = -(p1.dx-p2.dx)*sin_a+(p1.dy-p2.dy)*cos_a+(p1.r*p1.da+p2.r*p2.da)
        #相対的速度増分
        vn = +(p1.vx-p2.vx)*cos_a+(p1.vy-p2.vy)*sin_a
        vs = -(p1.vx-p2.vx)*sin_a+(p1.vy-p2.vy)*cos_a+(p1.r*p1.va+p2.r*p2.va)
        #合力
        p1.en[p2.n] += kn*un
        p1.es[p2.n] += ks*us
        
        hn = p1.en[p2.n] + etan*vn
        hs = p1.es[p2.n] + etas*vs
        
        if hn <= 0.0:
            #法線力がなければ、せん断力は0
            hs = 0.0
        elif abs(hs)-frc*hn >= 0.0:
            #摩擦力以上のせん断力は働かない
            hs = frc*abs(hn)*hs/abs(hs)     
        
        #合力
        p1.fx += -hn*cos_a + hs*sin_a
        p1.fy += -hn*sin_a - hs*cos_a
        p1.fm -=  p1.r*hs
    
    def clacStep(self):
        for p in self.pars:
            p.fx = 0
            p.fy = 0
            p.fm = 0
            
        self.calcForce()
        
        for p in self.pars:
            p.nextStep(self.dt)
            
        self.step += 1
    
class MainWindow(QtGui.QMainWindow):
    def __init__(self, *argv, **keywords ):
        super(MainWindow,self).__init__(*argv,**keywords)
        self.resize(320, 220)
        
        self.view = QtGui.QGraphicsView(self)
        self.scene = QtGui.QGraphicsScene(self.view)
        self.view.setScene(self.scene)
        self.setCentralWidget(self.view)
        
        self.pen = QtGui.QPen()
        self.pen.setColor(QtGui.QColor('#000000'))
        
        self.dem = DEM()
        
        self.step_limit = int(1/self.dem.dt*5)
        #self.step_limit = float("inf")
        print('limit',self.step_limit)
        self.output_step = 0
        self.output_interval = 10

        self.redraw()
        
        self.timer = QtCore.QTimer(self)
        self.timer.setInterval(int(1000*self.dem.dt))
        print('interval',int(1000*self.dem.dt))
        self.timer.timeout.connect(self.drawLoop)
        self.timer.start()
        

    def redraw(self):
        for item in self.scene.items():
            self.scene.removeItem(item)
            pass
        for p in self.dem.pars:
            self.scene.addEllipse(p.x-p.r,-p.y-p.r,p.r*2,p.r*2,self.pen)
            
        for l in self.dem.lines:
            self.scene.addLine(l.x1,-l.y1,l.x2,-l.y2,self.pen)
        
    def drawLoop(self):
        self.dem.clacStep()
        self.redraw()
        if self.dem.step % self.output_interval == 0:
            #self.saveJpeg()
            self.output_step += 1
        if self.dem.step >= self.step_limit:
            self.timer.stop()
            self.close()
        
        
    def saveJpeg(self):
        pix = QtGui.QPixmap(330,220)
        pix.fill(QtCore.Qt.white)
        paint = QtGui.QPainter(pix)
        
        self.scene.render(paint)#,QtCore.QRect(0,0,300,200))
        pix.save('c://Temp/dem/capture%03d.jpg' % self.output_step,'JPG')
        paint.end()

def main():
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です