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.
' ************************************************************************** ' *** ** ' *** 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