Анимация ЧМ-2014

В предверии к чемпионату мира по футболу 2014 года , была написана эта анимация. Пример хорош для понимания работы таких конструкции как макросы, Operator, заполнение типов, в общем для людей, изучающих freebasic думаю будет полезно.  

Платформы: Windows, Linux
Автор: dodicat

brazil.png

Type d2
    As Single x,y,z
    Dim As Single mw,ang
End Type

Type line3d
    As d2 v1,v2
End Type

'globals
Dim Shared As Integer xres,yres
Screenres 1000,700,32
Screeninfo xres,yres
Dim Shared As Any Pointer im
im=ImageCreate(xres,yres)
Dim Shared As Uinteger array(xres+1,yres+1)
'_________________________
Operator + (v1 As d2,v2 As d2) As d2
    Return Type<d2>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z,v1.mw+v2.mw,v1.ang+v2.ang)
End Operator

Operator -(v1 As d2,v2 As d2) As d2
    Return Type<d2>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z,v1.mw-v2.mw)
End Operator

Operator *(f As Single,v1 As d2) As d2 'scalar*d2
    Return Type<d2>(f*v1.x,f*v1.y,f*v1.z,f*v1.mw)
End Operator

Operator * (v1 As d2,v2 As d2) As Single 'dot product
    Return v1.x*v2.x+v1.y*v2.y+v1.z+v2.z',v1.mw*v2.mw
End Operator

Operator ^ (v1 As d2,v2 As d2) As d2 'cross product
    Return Type<d2>(v1.y*v2.z-v2.y*v1.z,-(v1.x*v2.z-v2.x*v1.z),v1.x*v2.y-v2.x*v1.y)
End Operator

#DEFINE dot *
#DEFINE cross ^
#DEFINE length(v) Sqr(v.x*v.x+v.y*v.y+v.z*v.z)
#DEFINE normalize(v) Type<d2>(v.x/length(v),v.y/length(v),v.z/length(v))
#DEFINE rr(f,l) (Rnd*(l-f)+f)

'locals
Dim As Integer n=5
Dim balls(1 To n) As d2
Dim direction(1 To n) As d2
Dim collision(n) As Integer
Dim dt As Single
Dim As d2 SA(1 To 54)
Dim As d2 B(1 To 41)
Dim As line3d linesegments(53)
Dim As Single cxSA,cySA,cxB,cyB
For n As Integer=1 To 54
    Read SA(n).x
    cxSA+=SA(n).x
Next n
For n As Integer=1 To 54
    Read SA(n).y
    cySA+=SA(n).y
Next n

For n As Integer=1 To 41
    Read B(n).x
    cxB+=B(n).x
Next n
For n As Integer=1 To 41
    Read B(n).y
    cyB+=B(n).x
Next n
cxSA=cxSA/54:cySA=cySA/54:
cxB=cxB/41:cyB=cyB/41:

For n As Integer=1 To 53
    linesegments(n).v1=Type<d2>(SA(n).x,SA(n).y)
    linesegments(n).v2=Type<d2>(SA(n+1).x,SA(n+1).y)
Next n
'================================================

Sub drawpolygon(p() As d2, col As Uinteger,im As Any Ptr=0)
    Dim k As Integer=UBound(p)+1
    Dim As Integer index,nextindex
    For n As Integer=1 To Ubound(p)
        index=n Mod k:nextindex=(n+1) Mod k
        If nextindex=0 Then nextindex=1
        Line im,(p(index).x,p(index).y)-(p(nextindex).x,p(nextindex).y),col
    Next
End Sub

Sub thickline(x1 As Double,_
    y1 As Double,_
    x2 As Double,_
    y2 As Double,_
    thickness As Double,_
    colour As Uinteger,_
    im As Any Pointer=0)
    Dim p As UInteger=RGB(255, 255, 254)
    If thickness<2 Then
        Line im,(x1,y1)-(x2,y2),colour
    Else
        Dim As Double h=Sqr((x2-x1)^2+(y2-y1)^2):If h=0 Then h=1e-6
        Dim As Double s= (y1-y2)/h ,c=(x2-x1)/h
        For x As Integer=1 To 2
            Line im,(x1+s*thickness/2,y1+c*thickness/2)-(x2+s*thickness/2,y2+c*thickness/2),p
            Line im,(x1-s*thickness/2,y1-c*thickness/2)-(x2-s*thickness/2,y2-c*thickness/2),p
            Line im,(x1+s*thickness/2,y1+c*thickness/2)-(x1-s*thickness/2,y1-c*thickness/2),p
            Line im,(x2+s*thickness/2,y2+c*thickness/2)-(x2-s*thickness/2,y2-c*thickness/2),p
            Paint im,((x1+x2)/2, (y1+y2)/2), p, p
            p=colour
        Next x
    End If
End Sub

Sub make_background
    Dim As Integer xres,yres
    Screeninfo xres,yres
    For y As Integer=0 To yres
        Line im,(0,y)-(xres,y),Rgb(0,0,50+y/8)
    Next y
End Sub

Function segmentdistance(l As Line3d,p As d2,Byref ip As d2=Type<d2>(0,0,0)) As Single
    Dim As Single linelength=length((l.v1-l.v2))
    Dim As Single dist= length((1/linelength)*((l.v1-l.v2) cross (p-l.v1)))
    Dim As Single lpf=length((p-l.v2)),lps=length((p-l.v1))
    If lps >= lpf Then
        Var temp=Sqr(lps*lps-dist*dist)/linelength
        If temp>=1 Then temp=1:dist=lpf
        ip=l.v1+temp*(l.v2-l.v1)
        Return dist
    Else
        Var temp=Sqr(lpf*lpf-dist*dist)/linelength
        If temp>=1 Then temp=1:dist=lps
        ip=l.v2+temp*(l.v1-l.v2)
        Return dist
    End If
    Return dist
End Function

Sub draw_balls(b As d2)
    #MACRO rotate(pivotx,pivoty,px,py,a,scale)
        Var Newx=scale*(Cos(a*.0174533)*(px-pivotx)-Sin(a*.0174533)*(py-pivoty))+pivotx
        Var Newy=scale*(Sin(a*.0174533)*(px-pivotx)+Cos(a*.0174533)*(py-pivoty))+pivoty
    #EndMacro
    #MACRO incircle(cx,cy,radius,x,y)
        (cx-x)*(cx-x) +(cy-y)*(cy-y)<= radius*radius
    #EndMacro
    If b.mw=0 Then b.mw=1
    b.mw=Abs(b.mw)
    Dim As Single dil
    For x As Integer=b.x-40 To b.x+40
        For y As Integer=b.y-40 To b.y+40
            If incircle(b.x,b.y,40,x,y) Then
                rotate(b.x,b.y,x,y,b.ang,dil)
                Var dist=Sqr((b.x-newx)*(b.x-newx)+(b.y-newy)*(b.y-newy))
                dil=(b.mw+(.5-b.mw)*dist/(40*b.mw))
                If incircle(b.x,b.y,(20*b.mw),newx,newy) Then
                    If x<=xres+1 AndAlso y<=yres+1 Then
                        Line(NewX-dil/2,NewY-dil/2)-(NewX+dil/2,NewY+dil/2),array(Abs(x),Abs(y)),BF
                    End If
                End If
            End If
        Next y
    Next x
End Sub
Function framecounter() As Integer
    Var t1=Timer,t2=t1
    Static As Double t3,frames,answer
    frames+=1
    If (t2-t3)>=1 Then t3=t2:answer=frames:frames=0
    Function= answer
End Function

Function Regulate(Byval MyFps As Integer,Byref fps As Integer) As Integer
    fps=framecounter()
    Static As Double timervalue,lastsleeptime
    Dim As Double delta
    Var k=fps-myfps,i=1/myfps
    If Abs(k)>1 Then delta+=i*Sgn(k)
    Var sleeptime=lastsleeptime+(i-Timer+timervalue)*2000+delta
    If sleeptime<1 Then sleeptime=1
    lastsleeptime=sleeptime
    timervalue=Timer
    Return sleeptime
End Function

Sub FullTime
    Type pt
        As Integer x,y
        As Uinteger col
    End Type
    #MACRO rot(pivot,p,scale)
        Type<pt>(scale*(p.x-pivot.x)+pivot.x,_
        scale*((p.y-pivot.y))+pivot.y)
    #EndMacro
    Dim As Integer _x,_y,cnt
    Screeninfo _x,_y
    Dim As pt a((_x+1)*(_y+1))
    For x As Integer=0 To _x
        For y As Integer=0 To _y
            a(cnt)=Type<pt>(x,y,Point(x,y))
            cnt+=1
        Next y
    Next x
    Dim As pt piv=Type<pt>(_x/2,_y/2)
    For dil As Single=1 To .05 Step -.05
        Screenlock
        Cls
        For n As Integer = 0 To Ubound(a)
            Var temp=rot(piv,a(n),dil)
            Pset(temp.x,temp.y),a(n).col
        Next n
        Screenunlock
    Next dil
    Sleep 1000
    End
End Sub
Sub tessilate(r As Single)
    Dim As Integer xres,yres
    Screeninfo xres,yres
    For y As Integer=0 To yres
        Line im,(0,y)-(xres,y),Rgb(200,y/3,0)
    Next y
    #MACRO _hex(p,r)
        Scope
            Dim flag As Byte
            Dim As Single lastx,lasty
            For z As Single=0 To 360 Step 360/6
                Var x=p.x+r*Cos(z*.0174533)
                Var y=p.y+r*Sin(z*.0174533)
                If flag =1 Then thickline(lastx,lasty,x,y,2,Rgb(y/4,0,255-y/4),im)
                lastx=x
                lasty=y
                flag=1
            Next z
        End Scope
    #EndMacro
    Dim As d2 pt
    Dim As Single x,y,z
    Dim As Integer k=1
    For x =0 To xres Step r+r/2
        z=3*k*r-yres/2
        For y =z To yres Step Sqr(3)*r
            pt=Type<d2>(x,y)
            _hex(pt,r)
        Next y
        k=-k
    Next x
End Sub
'_________draw background  to image then scan sub ________
Sub scan
    Dim As Integer xres,yres
    Screeninfo xres,yres
    tessilate 12
    Put(0,0),im,Pset
    For x As Integer=0 To xres-1
        For y As Integer=0 To yres-1
            array(x,y)=Point(x,y)
        Next y
    Next x
End Sub


Dim As Single seperation,temp

For z As Integer=1 To n 'set positions
    balls(z).x=rr(120,120)+seperation+300
    balls(z).y=rr(150,150)+seperation
    seperation=seperation+100
    balls(z).mw=Sqr(z)
    balls(z).ang=180
Next z
balls(n).mw=1.5
For z As Integer=1 To n 'set speeds
    temp=rr(.5,1.5)
    direction(z).x=temp
    direction(z).y=temp
    direction(z)=1.5*normalize(direction(z))
Next z

' _________Collision macros_____________________
Dim As d2 impulse,impact

#MACRO check_side_collisions()
    For z2 As Integer=1 To n
        #MACRO redirect()
            If collision(z2)=0 Then
                impact=-1*direction(z2)
                dt=impact dot impulse
                direction(z2)=direction(z2)+2*dt*impulse
                collision(z2)=1
            End If
        #EndMacro
        If balls(z2).x<20*balls(z2).mw Then
            impulse=Type<d2>(1,0)
            redirect()
        End If
        If balls(z2).x>xres-20*balls(z2).mw Then
            impulse=Type<d2>(-1,0)
            redirect()
        End If
        If balls(z2).y>yres-20*balls(z2).mw Then
            impulse=Type<d2>(0,-1)
            redirect()
        End If
        If balls(z2).y<20*balls(z2).mw Then
            impulse=Type<d2>(0,1)
            redirect()
        End If
    Next z2
#EndMacro

#MACRO check_ball_to_ball_collisions()
    For xx As Integer=1 To n
        For yy As Integer=1 To n
            If xx<>yy  Then
                If collision(xx)=0 OrElse collision(yy)=0 Then
                    If length((balls(xx)-balls(yy)))<=20*balls(xx).mw+20*balls(yy).mw Then
                        impulse=normalize((balls(xx)-balls(yy)))
                        impact=direction(xx)-direction(yy)
                        dt=(impact dot impulse)
                        Var mxx=balls(xx).mw*balls(xx).mw*balls(xx).mw 'the ball (weights)
                        Var myy=balls(yy).mw*balls(yy).mw*balls(yy).mw
                        direction(xx)=direction(xx)-dt*(2*myy/(mxx+myy))*impulse
                        direction(yy)=direction(yy)+dt*(2*mxx/(myy+mxx))*impulse
                        collision(xx)=1
                        collision(yy)=1
                        balls(xx).ang=balls(xx).ang+length(impact)'dt'5
                        balls(yy).ang=balls(yy).ang-length(impact)'dt'5
                    End If
                End If
            End If
        Next yy
    Next xx

#EndMacro
Dim As d2 impact2
#MACRO check_line_segment_collisions()
    For z3 As Integer=1 To Ubound(linesegments)
        For z2 As Integer=1 To n
            If collision(z2)=0 Then
                If segmentdistance(linesegments(z3),balls(z2),impact2)<20*balls(z2).mw Then
                    impulse=normalize((balls(z2)-impact2))
                    impact=-1*direction(z2)
                    dt=impact dot impulse
                    direction(z2)=direction(z2)+2*dt*impulse
                    collision(z2)=1
                End If
            End If
        Next z2
    Next z3
#EndMacro

#MACRO reset_stuff()
    If callcount Mod 2*n=0 Then collision(z)=0
    If balls(z).ang>180 Then balls(z).ang=balls(z).ang-.05
    If balls(z).ang<180 Then  balls(z).ang=balls(z).ang+.05
#EndMacro

Dim callcount As Integer
scan
make_background
drawpolygon(SA(),Rgb(200,0,0),im)
Paint im,(cxSA,cySA),Rgb(0,100,100),Rgb(200,0,0)
drawpolygon(B(),Rgb(200,200,0),im)
Paint im,(cxB,cyB),Rgb(0,100,0),Rgb(200,200,0)
Dim As Integer fps
Do
    callcount+=1
    If callcount>1e6 Then callcount=0
    check_line_segment_collisions()
    check_side_collisions()
    check_ball_to_ball_collisions()
    Screenlock
    Cls
    Put(0,0),im
    For z As Integer=1 To n
        balls(z)=balls(z)+direction(z)
        draw_balls(balls(z))
        reset_stuff()
    Next z
    Screenunlock
    Sleep Regulate(50,fps)
Loop Until InKey=Chr(27)

FullTime
Imagedestroy im

Sleep

Data _
407, 419, 443, 467, 482, 486, 471, 462, 460, 450, 436, 422, 410, 404, 392, 378, 363, 370, 351, 341, 336, 334, 333, 328, 323, 338, 331, 310, 302, 301, 300, 303, 305, 304, 297, 280, 267, 258, 250, 245, 245, 255, 258, 268, 279, 294, 300, 312, 325, 338, 352, 363, 378, 394


Data _
224, 237, 247, 256, 267, 284, 303, 314, 334, 354, 359, 365, 372, 388, 402, 420, 415, 430, 440, 450, 461, 472, 484, 497, 508, 520, 525, 515, 491, 465, 430, 406, 373, 343, 326, 316, 302, 286, 275, 268, 243, 218, 205, 183, 176, 168, 174, 179, 179, 179, 188, 195, 200, 205


Data _
376, 392, 400, 409, 433, 450, 463, 476, 487, 485, 474, 466, 462, 454, 444, 431, 410, 407, 389, 370, 385, 380, 368, 371, 357, 351, 332, 328, 306, 294, 284, 299, 307, 301, 309, 326, 333, 342, 352, 353, 359


Data _
224, 220, 213, 229, 244, 248, 254, 263, 275, 290, 300, 310, 326, 344, 356, 359, 369, 385, 403, 392, 372, 359, 349, 327, 319, 306, 295, 283, 289, 286, 273, 257, 241, 232, 227, 228, 218, 212, 207, 215, 224