import pygame, sys from pygame.locals import * from collections import deque from copy import copy from math import floor,ceil,pi,sin,cos,hypot,asin,atan from random import randrange def bezier(t,A,B,C): #if imaginary: # A,C=C,A P0=(A-B)*t+B P1=(B-C)*t+C return (P0-P1)*t+P1 def getangle(x0,y0,x1,y1,x2,y2): ly0=y0-y1 lx0=x0-x1 ly1=y2-y1 lx1=x2-x1 try: f0=ly0/lx0 f1=ly1/lx1 num=f1-f0 den=1-f1*f0 t=num/den except ZeroDivisionError: return pi/2 return atan(t) pygame.init() clocker=pygame.time.Clock() xr=400#rel yr=300#abs xcannon=xr/2 scannon=32 ycannon=yr-scannon DISPLAYSURF = pygame.display.set_mode((xr, yr)) pixObj = pygame.PixelArray(DISPLAYSURF) def spiral(sa,sb,distance): global reshalf yield sa,sb,1 for y in range(1,distance): mf=cos(y*pi/2/(distance-1)) #mf=1/(y+1) #print(mf) for x in range(4): for z in range(y): a=sa b=sb if x==0: b-=y-z a+=z elif x==1: a+=y-z b+=z elif x==2: b+=y-z a-=z elif x==3: a-=y-z b-=z yield a,b,mf def rel(a,c,b,d): e=b-a f=d-c return (e,f) def crossrel(a,c,b,d): e=b-a f=d-c return (-f,e) def bar(th,x,y,prev): xa,ya=crossrel(x,y,*prev) #print('was',xa,ya) xv,yv=x,y steep=abs(xa)>abs(ya) if steep: xv,yv=yv,xv xa,ya=ya,xa appd=xa/ya #print('became',xa,ya,appd) ret=[] for dis in range(th): f=1-(dis/th) d1=dis d2=-1-d1 if steep: ret.append((yv+d1,xv+(appd*d1),f)) ret.append((yv+d2,xv+(appd*d2),f)) else: ret.append((xv+(appd*d1),yv+d1,f)) ret.append((xv+(appd*d2),yv+d2,f)) #print('ret',ret) return ret def drawPixel(x,y,opaque,col,s2): global pixObj,xr,yr if 0<=x<xr and 0<=y<yr: temp=pygame.Color(pixObj[int(x)][int(y)])[1:] #print(temp) pixObj[int(x)][int(y)]=tuple((min(opaque*channel[0]+(1-opaque)*channel[1],255) for channel in zip(col,temp))) fancyPass=False fancyPrev=None def fancyDrawPixel(x,y,opaque,col,s2): global pixObj,xr,yr,fancyPass,fancyPrev if 0<=x<xr and 0<=y<yr: if opaque<=0: return opfancy=opaque**2 opaa=min(1,opaque*2) fancy=col fancyPass=(s2//4)%2 if fancyPass: fancy=tuple((int(min(sc*((sc+1)/512),255)) for sc in fancy)) temp=pygame.Color(pixObj[int(x)][int(y)])[1:] fancy=tuple((opaa*channel+(1-opaa)*channel/2 for channel in fancy)) pixObj[int(x)][int(y)]=tuple((max(min(opfancy*channel[0]+(1-opfancy)*channel[1],255),0) for channel in zip(fancy,temp))) precalc=dict() def drawBezierSegment(x0,y0,x1,y1,phase,rot,offset,th,override=drawPixel,step=1,color=(0,0,0),prev=None): xs=abs(x1-x0) ys=abs(y1-y0) ml=round(xs+ys)*4 mh=hypot(xs,ys) #first=True #.5,.5,1.5,1.5 #1,0,2,-1 #0,1,-1,2 #-.5,-.5,1,-1 #print(rot) xa=round(cos(rot),3)>0 ya=round(sin(rot),3)<0 xo=round(cos(offset),3)<0 yo=round(sin(offset),3)>0 xf=cos(phase) yf=sin(phase) #print(xa,ya,xf,yf,xo,yo) if xa^ya^yo^xo: xf,yf=-xf,-yf ## if yo: ## xf,yf=-yf,-xf ## if xo: ## xf,yf=yf,xf # if ((round(xa,3)<0)^(round(xf,3)<0)^(round(ya,3)<0)^(round(yf,3)<0))&(round(yf,3)<=0): # print('0') # xf,yf=xf+.5,yf+.5 # else: # xf,yf=xf-.5,yf-.5 # print('1') # xf,yf=-xf,-yf xf,yf=xf+.5,yf+.5 xts=(x1-x0) yts=(y1-y0) xm=xts*xf+(x1+x0)/2 ym=yts*yf+(y1+y0)/2 #cp=(th-1)/2 #if first: # print(x0,xm,x1,y0,ym,y1) # first=False #if th not in precalc and override in (drawPixel,fancyDrawPixel,): # precalc[th]=tuple(spiral(0,0,th)) # #print('once') xc=yc=-step s2=0 for s in range(ml):#increase res if needed t=1-s/ml x=bezier(t,x0,xm,x1) y=bezier(t,y0,ym,y1) if hypot(x-xc,y-yc)<step: continue #print('at least it does something') if override in (drawPixel,fancyDrawPixel,): #print('drawing') if prev is not None: #print('second',(th,x,y,prev)) if (th,x,y,prev) not in precalc: precalc[(th,x,y,prev)]=bar(th,x,y,prev) elif (th,x,y,prev) not in precalc: #print('first',(th,x,y,prev)) precalc[(th,x,y,prev)]=tuple(spiral(x,y,th)) else: precalc[(th,x,y,prev)]=((x,y,1),) for pixel in precalc[(th,x,y,prev)]: override(*pixel,color,s2) yield (*pixel,color,s2) xc,yc=x,y prev=(x,y) s2+=1 #drawPixel(x,y,1,(0,0,0)) def castRay(maxim,pos,dir): target=0 if dir>0: target=maxim mvm=(target-pos) try: return mvm/dir,mvm except ZeroDivisionError: return float('inf'),mvm def traceRay(tx,ty): global xr,yr,xcannon,ycannon x,y=xcannon,ycannon yield(x,y) for depth in range(5): castx,movex=castRay(xr,x,tx) casty,movey=castRay(yr,y,ty) if castx<casty: x=x+movex y=round(y+movex/tx*ty) else: y=y+movey x=round(x+movey/ty*tx) yield(x,y) if x in (0,xr): tx=-tx elif y in (0,yr): ty=-ty linecache=dict() spircache=dict() def linear(line,step): xl=line[0][0]-line[1][0] yl=line[0][1]-line[1][1] il=int((xl*xl+yl*yl)**.5/step) for p in range(il): f=p/il af=1-f x=line[1][0]*f+line[0][0]*af y=line[1][1]*f+line[0][1]*af yield x,y def lines(co,th,color,step,visible=True): global linecache,spircache ret=[] for line in zip(co[:-1],co[1:]): lineargs=(line,step) if lineargs not in linecache: linecache[lineargs]=tuple(linear(*lineargs)) if visible: if th not in spircache: spircache[th]=tuple(spiral(0,0,th)) for pixel in ret: for offs in spircache[th]: drawPixel(pixel[0]+offs[0],pixel[1]+offs[1],1,color,0) ret.extend(linecache[lineargs]) return ret curveargs=dict() def placebo(*args,**kwargs): pass trC=dict() def toRelative(x,y): global xr,yr,trC if (x,y) in trC: return trC[(x,y)] trC[(x,y)]=(x/xr,y/yr) return trC[(x,y)] frC=dict() def fromRelative(x,y): global xr,yr,frC if (x,y) in frC: return frC[(x,y)] frC[(x,y)]=(x*xr,y*yr) return frC[(x,y)] def drawBezierCurve(angle,co,th,override=drawPixel,step=1,color=(0,0,0),relative=False,tray=True): global curveargs,yr #print('drawing :-j') if relative: co=[fromRelative(*s) for s in co] th=int(th*yr) if override not in (drawPixel,fancyDrawPixel,): th=1 tray=False curveparam=(angle,tuple((tuple(s) for s in co)),step,tray) if curveparam not in curveargs: curveargs[curveparam]=[] phase=0 rot=0 offset=0 prev=None ppix=None for a,b in zip(co[:-1],co[1:]): if prev is not None: rot+=abs(getangle(*prev,*a,*b)) rot%=pi*2 phase%=pi*2 rp=round(rot*2/pi) pp=round(phase*2/pi) while pp<rp: offset-=angle phase+=angle rp=round(rot*2/pi) pp=round(phase*2/pi) # while pp>rp: # offset+=angle # phase-=angle # rp=round(rot*2/pi) # pp=round(phase*2/pi) for pixabs in drawBezierSegment(*a,*b,phase,rot,offset,th,override=override,step=step,color=color,prev=ppix): curveargs[curveparam].append(pixabs) phase+=angle prev=a if tray: for pixel in spiral(*co[-1],th*16): curveargs[curveparam].append((*pixel,color,0)) override(*pixel,color,0) else: for pixabs in curveargs[curveparam]: override(*pixabs) return curveargs[curveparam] pathscache=dict() def getinterpco(angle,co,relative): global pathscache curveparam=(angle,tuple((tuple(s) for s in co)),relative) if curveparam not in pathscache: pathscache[curveparam]=[tuple((int(sc) for sc in dsc[:2])) for dsc in drawBezierCurve(angle,co,1,override=placebo,step=1,relative=relative,tray=False)] return pathscache[curveparam] pygame.display.set_caption('Стрелялка') with open('level1.txt','r') as f: curves=eval(f.read()) hpi=pi/2 colors=[(0,255,0),(0,255,255),(255,0,0),(255,0,255),(255,255,0)] ray=(1,0) circcos=[getinterpco(hpi,co,True) for co in curves] #pygame.draw.circle() circinds=[deque() for co in curves] addtick=0 speed=1 sz=16 szr=sz//2 while True: if addtick>=sz: addtick=0 for i in range(len(circinds)): circinds[i].appendleft(randrange(len(colors))) for event in pygame.event.get(): if event.type == QUIT or speed>255: pygame.quit() sys.exit() elif event.type == pygame.MOUSEMOTION: ray=rel(xcannon,ycannon,*event.pos) if event.type == pygame.MOUSEBUTTONUP: pass DISPLAYSURF.fill((255,255,255)) drawBezierCurve(hpi,[(xcannon-scannon,ycannon),(xcannon+scannon,ycannon)],3,color=(0,0,0),step=.3,tray=False) drawBezierCurve(hpi,[(xcannon,ycannon-scannon),(xcannon,ycannon+scannon)],3,color=(0,0,0),step=.3,tray=False) for i,curve in enumerate(curves): drawBezierCurve(hpi,curve,.01,color=colors[i%len(colors)],step=.3,override=fancyDrawPixel,relative=True) for i,co in enumerate(circcos): for pos,c in enumerate(circinds[i]): posi=pos*sz+addtick if posi<len(co): pygame.draw.circle(DISPLAYSURF,colors[c],co[posi],szr) #print('ball at',i,'color',colors[c],'place',co[posi]) else: speed+=1 lines(tuple(traceRay(*ray)),2,color=(255,128,0),step=5) pygame.display.update() addtick+=speed clocker.tick(30) |