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