Nucleation and Growth

This stemmed from an attempt to model the situation when ( say) water vapour condenses ( or freezes) onto a cold window, or a liquid metal cools and solidifies. Each area starts and grows independently.

I had to use tricks to make sure that a growing crystal did not overwrite existing ones, and it is still not perfect. I used direct drawing of points on-a-line, using Bresenham's algorithm, because I had at every point to first look at whether that next pixel was already condensed and growing.

The animation above is of course faster than the actual program runs.

You can also try growing isotropic crystals, which grow as expanding circles; or anisotropically as triangles or squares. I liked the colouring scheme I used, which looks like a micrograph of freezing brass, and makes a nice pattern for tiling.

-

LB code used, with variations...



    '   **************************************************************************
    '   ***                                                                     **
    '   ***     condensingShapess      22 July 2016        tenochtitlanuk      **
    '   ***                                                                     **
    '   **************************************************************************


    '   Rain freezes on glass from nucleation sites starting at random intervals...



    nomainwin

    global pi, TX, TY, Ttheta, hdc, k, kk

    dim drop$( 200)

    TX          =400: TY =350: Ttheta =0 '   screen centre, pointing North/up.
    pi          =4 *atn( 1)

    move        =1
    'shape$      ="6," +str$( (0.75^0.5) /2) +", 150,         1,120,   1,120,    1,120"                                 ' equilateral triangle, reached from its centre.
    'shape$      ="5,  1, 135,         1, 90,   1, 90,    1, 90,    1, 90"                       ' squares.
    shape$      ="6,  1, 120,         1, 60,   1, 60,    1, 60,    1, 60,   1, 60,    1, 60"    ' hexagon.

    m =1                                                           ' 4 moves- halfstep, turn 150, down, then 3 times fullstep, turn 120

    WindowWidth  =1000: WindowHeight =740

    'texteditor #w.te1, 10, 620, 804,  50   '   for debugging to see values..

    graphicbox #w.gb1,  10, 10, 804, 604
    graphicbox #w.gb2, 840, 10, 100, 100

    menu #w, "File", "Save", [saveI]

    button #w.b1, "Save", saveImage, LR, 50, 50

    open "Growing frosty windows." for window as #w

        #w    "trapclose quit"

        h       = hwnd( #w.gb1)
        calldll #user32, "GetDC", h as ulong, hdc as ulong

        #w.gb1 "down ; fill 1 0 0 ; flush"
        #w.gb1 "size 1"
        #w.gb2 "font DejaVu_Serif 8 bold"
        #w.gb2 "down ; fill red ; color black ; backcolor red ; flush"

timer 1000, [p2]
wait
[p2]
timer 0

'   **************************************************************************

[makeDrops]
            R           =int( 150 +100 *cosRad( k *50))
            G           =int( 100 +250 *rnd( 1))
            B           =255 -R
            x           =int(     800 *rnd( 1))
            y           =int(     600 *rnd( 1))
            ra          =2
            drop$( 1)   =str$( x) +"," +str$( y) +"," +right$( "000" +str$( ra), 3) +"," +str$( R +G *2^8 +B *2^16)  +"," +str$( int( 181 *rnd( 1)))
timer 1000, [p[
wait
[p]
timer 0

[mainLoop]
        for kk =0 to 4000                               '   repeat update of screen 2000 times...
            for c =1 to m                             '       ...for each of up to 100 growing zones.
                lC     =val( word$( drop$( c), 4, ",")) '   x, y, ra, lColor, angle
                angle  =val( word$( drop$( c), 5, ","))

                #w.gb1 "color "; MakeRGB$( lC)
                x       =val( word$( drop$( c), 1, ","))
                y       =val( word$( drop$( c), 2, ","))
                radius  =val( word$( drop$( c), 3, ","))

                if radius =0 then [skip]

                call display shape$, radius /2, x, y, angle
                r1      =instr( drop$( c), ",", 1)
                rStart  =instr( drop$( c), ",", r1 +1)
                rFinis  =instr( drop$( c), ",", rStart +1)

                drop$( c) =left$( drop$( c), rStart) +right$( "000" +str$( radius +1), 3) +mid$( drop$( c), rFinis)

              [skip]
                scan
            next c

            #w.gb1 "getbmp sc 0 0 804 604"                  '   so redraws don't overflow!
            #w.gb1 "cls"
            #w.gb1 "drawbmp sc 0 0"

            if rnd( 1) <0.20 then                           '   try to nucleate a new site 20 times in every 100 on average
                m           =m +1
                R           =int( 150 +100 *cosRad( k *50))
                G           =int( 100 +250 *rnd( 1))
                B           =255 -R

    [again]     x           =int( 800 *rnd( 1))
                y           =int( 600 *rnd( 1))
                if getPixel( x, y) <>1 then [again]

                ra          =2
                drop$( m)   =str$( x) +"," +str$( y) +"," +right$( "000" +str$( ra), 3) +"," +str$( R +G *2^8 +B *2^16)  +"," +str$( int( 181 *rnd( 1)))

            end if

            #w.gb2 "cls ; fill red ; color black ; up ; goto 20 30 ; down"
            #w.gb2 "\ " +right$( "00000" +str$( kk), 5) +" ";
            #w.gb2 "up ; goto 20 60 ; down"
            #w.gb2 "\ " +right$( "     " +str$( m -1), 5) +" ";


            if kk mod 20 =0 then
                #w.gb1 "getbmp sc 0 0 804 604"
                bmpsave "sc", "shape" +right$( "0000" +str$( kk), 4) +".bmp"
            end if

        next kk

        #w.gb1 "flush"


'   ******************************************************************************************
        wait
'   ____________________________________________________________
    function MakeRGB( red, green, blue)
        if red   <  0 then red   =  0
        if red   >255 then red   =255
        if green <  0 then green =  0
        if green >255 then green =255
        if blue  <  0 then blue  =  0
        if blue  >255 then blue  =255
        MakeRGB     =( blue *256 *256) +( green *256) +red
    end function

    function MakeRGB$( cc)
        blu         =int(  cc /256 /256)
        grn         =int( ( cc -blu *256 *256) / 256)
        red         =int( cc -blu *256 *256 -grn *256)
        MakeRGB$    =str$( red) +" " +str$( grn) +" " +str$( blu)
    end function

    function longCol( c$)
        r           =val( word$( c$, 1, " "))
        g           =val( word$( c$, 2, " "))
        b           =val( word$( c$, 3, " "))
        longCol     =r +g *2^8 +b *2^16
    end function

    function sinRad( a)
        sinRad  =sin( a *pi /180)
    end function

    function cosRad( a)
        cosRad  =cos( a *pi /180)
    end function

    sub draw lifted, x, y
        if lifted =0 then #w.gb1 "up" else #w.gb1 "down"
        #w.gb1 "line "; TX; " "; TY; " "; x; " "; y
        Ttheta  =atan2( x -TX, TY -y) *180 /pi  '   NB DEGREES.
        TX      =x: TY      =y
    end sub

    sub turn angle  '   increment/update global turtle direction ( in DEGREES)
        Ttheta =( Ttheta +angle) mod 360
    end sub

    sub forward state, s
        dx      =s *cosRad( Ttheta)
        dy      =s *sinRad( Ttheta)
        if state =0 then #w.gb1 "up" else #w.gb1 "down"
        #w.gb1 "line "; TX; " "; TY; " "; TX +dx; " "; TY +dy; " ; up"
        TX      =TX +dx
        TY      =TY +dy
    end sub

    function atan2( x, y)
        Result$ = "Undetermined"
        If ( x = 0) and ( y > 0) Then atan2 = pi / 2:     Result$ = "Determined"
        If ( x = 0) and ( y < 0) Then atan2 = 3 * pi / 2: Result$ = "Determined"
        If ( x > 0) and ( y = 0) Then atan2 = 0:          Result$ = "Determined"
        If ( x < 0) and ( y = 0) Then atan2 = pi:         Result$ = "Determined"
        If Result$ = "Determined" Then [End.of.function]
        BaseAngle   = Atn( abs( y) /abs( x))
        If (x > 0) and (y > 0) Then atan2 =        BaseAngle
        If (x < 0) and (y > 0) Then atan2 = pi    -BaseAngle
        If (x < 0) and (y < 0) Then atan2 = pi    +BaseAngle
        If (x > 0) and (y < 0) Then atan2 = 2*pi  -BaseAngle
       [End.of.function]
    end function

    sub graticule
        for x =0 to 800 step 100    '   draw vertical graticule lines
            #w.gb1 "line "; x; " "; 0; " "; x;   " "; 700
        next x
        for y =0 to 700 step 100
            #w.gb1 "line "; 0; " "; y; " "; 800; " "; y
        next y
    end sub

  [save]
    #w.gb1 "getbmp scr 0 0 800 700"
    filedialog "Save as ", "*.bmp", fn$
    bmpsave "scr", fn$
    wait

    sub quit h$
        end
        calldll #user32, "ReleaseDC", hw as ulong, hdc as ulong   'release the DC
        close #h$
        end
    end sub

    sub display shape$, scale, x, y, angle
        noOfTerms   =val( word$( shape$, 1, ","))
        vStep       =val( word$( shape$, 2, ",")) *scale

        Tx          =x +vStep *sinRad( angle)
        Ty          =y -vStep *cosRad( angle)

        #w.gb1 "up ; goto "; Tx; " "; Ty; " ; down"

        Ttheta      =angle

        for i =2 to noOfTerms +1
            vAngle      =val( word$( shape$, 2 *i -1, ","))
            vStep       =val( word$( shape$, 2 *i   , ",")) *scale

            Ttheta      =Ttheta +vAngle
            TxN         =Tx +vStep *sinRad( Ttheta)
            TyN         =Ty -vStep *cosRad( Ttheta)

            call bresenham int( Tx), int( Ty), int( TxN), int( TyN) ' replaces 'line Tx Ty TxN TyN')
            Tx          =TxN
            Ty          =TyN
        next i

    end sub

    sub delay t
        timer t, [cont]
        wait
      [cont]
        timer 0
    end sub

    function getPixel( x, y)
        x =int( x)
        y =int( y)
        calldll #gdi32, "GetPixel", hdc as ulong, x as long, y as long, getPixel as ulong
        'getPixel =pixcol
    end function

    function getPixel$( x, y)
        calldll #gdi32, "GetPixel", hdc as ulong, x as long, y as long, pixcol as ulong
        blu         =int(  pixcol /256 /256)
        grn         =int( (pixcol -blu *256 *256) / 256)
        red         =int(  pixcol -blu *256 *256 -grn *256)
        getPixel$   =str$( red) +" " +str$( grn) +" " +str$( blu)
    end function


    sub saveImage h$
        #w.gb1 "getbmp scr 0 0 804 604"
        bmpsave "scr", "freezeShape" +right$( "0000" +str$( k), 4) +".bmp"
    end sub

[saveI]
    #w.gb1 "getbmp scr 0 0 804 604"
    bmpsave "scr", "freezeShape" +right$( "0000" +str$( k), 4) +".bmp"
    wait

    sub bresenham x1, y1, x2, y2    '    Inputs are integers x1, y1, x2, y2: destroys value of x1, y1
        dx  = abs( x2 - x1): sx = -1: if x1 < x2 then sx = 1
        dy  = abs( y2 - y1): sy = -1: if y1 < y2 then sy = 1

        er  = 0 -dy: if dx > dy then er = dx
        er  = int( er / 2)

[more]  g   = getPixel( x1, y1)     '   can only write on longcolor 1
        if g = 1 then #w.gb1 "set "; x1; " "; y1
        scan
        if ( ( x1 = x2) and ( y1 = y2))                              then exit sub
        e2  = er
        if ( e2 > 0 -dx) then er = ( er - dy): x1 = ( x1 + sx)
        if ( e2 <    dy) then er = ( er + dx): y1 = ( y1 + sy)
        goto [more]

    end sub

    sub ellipse x, y, Major, Minor, inclination, bg
        xx      =Minor *cosRad( 0)
        yy      =Major *sinRad( 0)
        xs      =x +xx *cosRad( inclination) -yy *sinRad( inclination)
        ys      =y +xx *sinRad( inclination) +yy *cosRad( inclination)
        #w.gb1 "goto "; xs; " "; ys
        #w.gb1 "down"

        dAngle  =1 /( Major +Minor)'   bodge to speed up small circles and not leave gaps at large radii.
        for angle =0 to 360 step 1  '   make this bigger at small radius and smaller at large radii
                                    '   at present dAngle =0.1 is very slow but =1 leaves gaps.... which then can be filled with wrong colour!
            xx      =Minor *cosRad( angle)
            yy      =Major *sinRad( angle)
            xs      =x +xx *cosRad( inclination) -yy *sinRad( inclination)
            ys      =y +xx *sinRad( inclination) +yy *cosRad( inclination)
            'print getPixel( int( xs), int( ys)), getPixel$( int( xs), int( ys))
            g       = getPixel( int( xs), int( ys))     '   can only write on longcolor 1 or own colour...
            if ( g =1) or ( g =bg) then #w.gb1 "set "; int( xs); " "; int( ys)
        next angle

        #w.gb1 "up"
    end sub