布朗树(Brownian tree)【代码实现】

C

#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <FreeImage.h>
 
#define NUM_PARTICLES  1000
#define SIZE           800
 
void draw_brownian_tree(int world[SIZE][SIZE]){
  int px, py; // particle values
  int dx, dy; // offsets
  int i;
 
  // set the seed
  world[rand() % SIZE][rand() % SIZE] = 1;
 
  for (i = 0; i < NUM_PARTICLES; i++){
    // set particle's initial position
    px = rand() % SIZE;
    py = rand() % SIZE;
 
    while (1){
      // randomly choose a direction
      dx = rand() % 3 - 1;
      dy = rand() % 3 - 1;
 
      if (dx + px < 0 || dx + px >= SIZE || dy + py < 0 || dy + py >= SIZE){
        // plop the particle into some other random location
        px = rand() % SIZE;
        py = rand() % SIZE;
      }else if (world[py + dy][px + dx] != 0){
        // bumped into something
        world[py][px] = 1;
        break;
      }else{
        py += dy;
        px += dx;
      }
    }
  }
}

int main(){
  int world[SIZE][SIZE];
  FIBITMAP * img;
  RGBQUAD rgb;
  int x, y;
 
  memset(world, 0, sizeof world);
  srand((unsigned)time(NULL));
 
  draw_brownian_tree(world);
 
  img = FreeImage_Allocate(SIZE, SIZE, 32, 0, 0, 0);
 
  for (y = 0; y < SIZE; y++){
    for (x = 0; x < SIZE; x++){
      rgb.rgbRed = rgb.rgbGreen = rgb.rgbBlue = (world[y][x] ? 255 : 0);
      FreeImage_SetPixelColor(img, x, y, &rgb);
    }
  }
  FreeImage_Save(FIF_BMP, img, "brownian_tree.bmp", 0);
  FreeImage_Unload(img);
}

C++

#include <windows.h>
#include <iostream>
#include <string>
 
//--------------------------------------------------------------------
using namespace std;
 
//--------------------------------------------------------------------
enum states { SEED, GROWING, MOVING, REST };
enum treeStates { NONE, MOVER, TREE };
const int MAX_SIDE = 480, MAX_MOVERS = 511, MAX_CELLS = 15137;
 
//--------------------------------------------------------------------
class point
{
public:
    point()                  { x = y = 0; }
    point( int a, int b )    { x = a; y = b; }
    void set( int a, int b ) { x = a; y = b; }
 
    int x, y;
};
//--------------------------------------------------------------------
class movers
{
public:
    point pos;
    bool moving;
    movers() : moving( false ){}
};
//--------------------------------------------------------------------
class myBitmap
{
public:
    myBitmap() : pen( NULL ) {}
    ~myBitmap()
    {
	DeleteObject( pen );
	DeleteDC( hdc );
	DeleteObject( bmp );
    }
 
    bool create( int w, int h )
    {
	BITMAPINFO	bi;
	ZeroMemory( &bi, sizeof( bi ) );
 
	bi.bmiHeader.biSize        = sizeof( bi.bmiHeader );
	bi.bmiHeader.biBitCount	   = sizeof( DWORD ) * 8;
	bi.bmiHeader.biCompression = BI_RGB;
	bi.bmiHeader.biPlanes	   = 1;
	bi.bmiHeader.biWidth	   =  w;
	bi.bmiHeader.biHeight	   = -h;
 
	HDC dc = GetDC( GetConsoleWindow() );
	bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 );
	if( !bmp ) return false;
 
	hdc = CreateCompatibleDC( dc );
	SelectObject( hdc, bmp );
	ReleaseDC( GetConsoleWindow(), dc ); 
 
	width = w; height = h;
 
	return true;
    }
 
    void clear()
    {
	ZeroMemory( pBits, width * height * sizeof( DWORD ) );
    }
 
    void setPenColor( DWORD clr )
    {
	if( pen ) DeleteObject( pen );
	pen = CreatePen( PS_SOLID, 1, clr );
	SelectObject( hdc, pen );
    }
 
    void saveBitmap( string path )
    {
	BITMAPFILEHEADER fileheader;
	BITMAPINFO	 infoheader;
	BITMAP		 bitmap;
	DWORD*		 dwpBits;
	DWORD		 wb;
	HANDLE		 file;
 
	GetObject( bmp, sizeof( bitmap ), &bitmap );
 
	dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
	ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );
	ZeroMemory( &infoheader, sizeof( BITMAPINFO ) );
	ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );
 
	infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
	infoheader.bmiHeader.biCompression = BI_RGB;
	infoheader.bmiHeader.biPlanes = 1;
	infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader );
	infoheader.bmiHeader.biHeight = bitmap.bmHeight;
	infoheader.bmiHeader.biWidth = bitmap.bmWidth;
	infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );
 
	fileheader.bfType    = 0x4D42;
	fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER );
	fileheader.bfSize    = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;
 
	GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );
 
	file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL,
                           CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL );
	WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL );
	WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL );
	CloseHandle( file );
 
	delete [] dwpBits;
    }
 
    HDC getDC()     { return hdc; }
    int getWidth()  { return width; }
    int getHeight() { return height; }
 
private:
    HBITMAP bmp;
    HDC	    hdc;
    HPEN    pen;
    void    *pBits;
    int	    width, height;
};
//--------------------------------------------------------------------
class brownianTree
{
public:
    brownianTree()         
    { 
	_bmp.create( MAX_SIDE, MAX_SIDE );
	init(); 
    }
 
    void init()
    {
	_cellCount = 0;
	ZeroMemory( _grid, sizeof( _grid ) );
	_bmp.clear();
	_state = SEED;
    }
 
bool mainLoop()
    {
	switch( _state )
	{
	    case REST:    saveTree(); return false;
	    case SEED:    doSeed(); break;
	    case GROWING: startMovers(); break;
	    case MOVING:  moveMovers();
	}
	    return true;
	}
 
    myBitmap* getBmp() { return &_bmp; }
 
private:
    void saveTree()
    {
	for( int y = 0; y < MAX_SIDE; y++ )
	    for( int x = 0; x < MAX_SIDE; x++ )
		if( _grid[x][y] == TREE )
		    SetPixel( _bmp.getDC(), x, y, RGB( 255, 120, 0 ) );
 
        _bmp.saveBitmap( "f:\\rc\\tree.bmp" );
    }
 
    void doSeed()
    {
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4;
	_grid[rand() % x + y][rand() % x + y] = TREE;
	_cellCount++;
	_state = GROWING;
    }
 
    void addMover( movers* m )
    {
	m->moving = true;
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4, a, b;
	while( true )
	{
	    a = rand() % x + y; b = rand() % x + y;
	    if( _grid[a][b] == NONE ) break;
	}
 
	m->pos.set( a, b );
	_grid[a][b] = MOVER;
    }
 
    void startMovers()
    {
	movers* m;
	for( int c = 0; c < MAX_MOVERS; c++ )
	{
	    m = &_movers[c];
	    addMover( m );
	}
	_state = MOVING;
    }
 
    void addToTree( movers* m )
    {
	m->moving = false;
	_grid[m->pos.x][m->pos.y] = TREE;
	if( ++_cellCount >= MAX_CELLS ) _state = REST;
 
	COORD c = { 0, 1 };
	SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), c );
	cout << "Cells added: " << _cellCount
             << " from " << MAX_CELLS << " => "
             <<  static_cast<float>( 100 * _cellCount ) /
                 static_cast<float>( MAX_CELLS )
             << "%              ";
    }
 
    bool moveIt( movers* m )
    {
	point f[8]; int ff = 0;
	for( int y = -1; y < 2; y++ )
	{
	    for( int x = -1; x < 2; x++ )
	    {
		if( !x && !y ) continue;
		int a = m->pos.x + x, b = m->pos.y + y;
		if( a < 0 || b < 0 || a >= MAX_SIDE || b >= MAX_SIDE )
		{
		    addToTree( m );
		    return true;
		}
		switch( _grid[a][b] )
		{
		    case TREE:
			addToTree( m );
			return true;
		    case NONE:
			f[ff++].set( a, b );
		}
	    }
        }
 
	if( ff < 1 ) return false;
 
	_grid[m->pos.x][m->pos.y] = NONE;
	m->pos = f[rand() % ff];
	_grid[m->pos.x][m->pos.y] = MOVER;
 
	return false;
    }
 
    void moveMovers()
    {
	movers* mm;
	for( int m = 0; m < MAX_MOVERS; m++ )
	{
	    mm = &_movers[m];
	    if( !mm->moving ) continue;
	    if( moveIt( mm ) && _cellCount < MAX_CELLS ) addMover( mm );
	}
    }
 
    states   _state;
    BYTE     _grid[MAX_SIDE][MAX_SIDE];
    myBitmap _bmp;
    int      _cellCount;
    movers   _movers[MAX_MOVERS];
};

int main( int argc, char* argv[] )
{
    ShowWindow( GetConsoleWindow(), SW_MAXIMIZE );
    srand( GetTickCount() );
 
    brownianTree tree;
 
    DWORD now = GetTickCount();
    while( tree.mainLoop() );
 
    now = GetTickCount() - now;
    cout << endl << endl << "It took "
         << now / 1000
         << " seconds to complete the task!" << endl << endl;
 
    BitBlt( GetDC( GetConsoleWindow() ), 20, 90, MAX_SIDE, MAX_SIDE,
            tree.getBmp()->getDC(), 0, 0, SRCCOPY );
 
    system( "pause" );
    return 0;
}

C#

using System;
using System.Drawing;
 
namespace BrownianTree
{
    class Program
    {
        static Bitmap BrownianTree(int size, int numparticles)
        {
            Bitmap bmp = new Bitmap(size, size);
            Rectangle bounds = new Rectangle { X = 0, Y = 0, Size = bmp.Size };
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.Black);
            }
            Random rnd = new Random();
            bmp.SetPixel(rnd.Next(size), rnd.Next(size), Color.White);
            Point pt = new Point(), newpt = new Point();
            for (int i = 0; i < numparticles; i++)
            {
                pt.X = rnd.Next(size);
                pt.Y = rnd.Next(size);
                do
                {
                    newpt.X = pt.X + rnd.Next(-1, 2);
                    newpt.Y = pt.Y + rnd.Next(-1, 2);
                    if (!bounds.Contains(newpt))
                    {
                        pt.X = rnd.Next(size);
                        pt.Y = rnd.Next(size);
                    }
                    else if (bmp.GetPixel(newpt.X, newpt.Y).R > 0)
                    {
                        bmp.SetPixel(pt.X, pt.Y, Color.White);
                        break;
                    }
                    else
                    {
                        pt = newpt;
                    }
                } while (true);
            }
            return bmp;
        }
 
        static void Main(string[] args)
        {
            BrownianTree(300, 3000).Save("browniantree.png");
        }
    }
}

Go

package main
 
import (
    "fmt"
    "image"
    "image/color"
    "image/png"
    "math/rand"
    "os"
)
 
const w = 400     // image width
const h = 300     // image height
const n = 15000   // number of particles to add
const frost = 255 // white
 
var g *image.Gray
 
func main() {
    g = image.NewGray(image.Rectangle{image.Point{0, 0}, image.Point{w, h}})
    // off center seed position makes pleasingly asymetrical tree
    g.SetGray(w/3, h/3, color.Gray{frost})
generate:
    for a := 0; a < n; {
        // generate random position for new particle
        rp := image.Point{rand.Intn(w), rand.Intn(h)}
        if g.At(rp.X, rp.Y).(color.Gray).Y == frost {
            // position is already set.  find a nearby free position.
            for {
                rp.X += rand.Intn(3) - 1
                rp.Y += rand.Intn(3) - 1
                // execpt if we run out of bounds, consider the particle lost.
                if !rp.In(g.Rect) {
                    continue generate
                }
                if g.At(rp.X, rp.Y).(color.Gray).Y != frost {
                    break
                }
            }
        } else {
            // else particle is in free space.  let it wander
            // until it touches tree
            for !hasNeighbor(rp) {
                rp.X += rand.Intn(3) - 1
                rp.Y += rand.Intn(3) - 1
                // but again, if it wanders out of bounds consider it lost.
                if !rp.In(g.Rect) {
                    continue generate
                }
            }
        }
        // x, y now specify a free position toucing the tree.
        g.SetGray(rp.X, rp.Y, color.Gray{frost})
        a++
        // progress indicator
        if a%100 == 0 {
            fmt.Println(a, "of", n)
        }
    }
    f, err := os.Create("tree.png")
    if err != nil {
        fmt.Println(err)
        return
    }
    err = png.Encode(f, g)
    if err != nil {
        fmt.Println(err)
    }
    f.Close()
}
 
var n8 = []image.Point{
    {-1, -1}, {-1, 0}, {-1, 1},
    {0, -1}, {0, 1},
    {1, -1}, {1, 0}, {1, 1}}
 
func hasNeighbor(p image.Point) bool { 
    for _, n := range n8 {
        o := p.Add(n)
        if o.In(g.Rect) && g.At(o.X, o.Y).(color.Gray).Y == frost {
            return true
        }
    }
    return false
}

Java

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.*;
import javax.swing.JFrame;
 
public class BrownianTree extends JFrame implements Runnable {
 
    BufferedImage I;
    private List<Particle> particles;
    static Random rand = new Random();
 
    public BrownianTree() {
        super("Brownian Tree");
        setBounds(100, 100, 400, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        I = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        I.setRGB(I.getWidth() / 2, I.getHeight() / 2, 0xff00);
        particles = new LinkedList<Particle>();
    }
 
    @Override
    public void paint(Graphics g) {
        g.drawImage(I, 0, 0, this);
    }
 
    public void run() {
        for (int i = 0; i < 20000; i++) {
            particles.add(new Particle());
        }
        while (!particles.isEmpty()) {
            for (Iterator<Particle> it = particles.iterator(); it.hasNext();) {
                if (it.next().move()) {
                    it.remove();
                }
            }
            repaint();
        }
    }
 
    public static void main(String[] args) {
        BrownianTree b = new BrownianTree();
        b.setVisible(true);
        new Thread(b).start();
    }
 
    private class Particle {
 
        private int x, y;
 
        private Particle() {
            x = rand.nextInt(I.getWidth());
            y = rand.nextInt(I.getHeight());
        }
 
        /* returns true if either out of bounds or collided with tree */
        private boolean move() {
            int dx = rand.nextInt(3) - 1;
            int dy = rand.nextInt(3) - 1;
            if ((x + dx < 0) || (y + dy < 0)
                    || (y + dy >= I.getHeight()) || (x + dx >= I.getWidth())) {
                return true;
            }
            x += dx;
            y += dy;
            if ((I.getRGB(x, y) & 0xff00) == 0xff00) {
                I.setRGB(x - dx, y - dy, 0xff00);
                return true;
            }
            return false;
        }
    }
}

替代版本,输出到图片

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
 
public class BasicBrownianTree {
 
    private int pixelsLost;
    private Point p;
    private Point nextP;
    private int pixelCount;
    private int width;
    private int height;
    private int color;
    private BufferedImage img;
 
    public BasicBrownianTree( int argb, int size, double density ) {
        pixelsLost = 0;
        p = new Point();
        nextP = new Point();
        width = size;
        height = size;
        color = argb;
        pixelCount = (int) ( width * height * density );
        img = new BufferedImage( width, height, BufferedImage.TYPE_INT_ARGB );
    }
 
    public void generate() {
        // print text to the console
        System.out.println( "Drawing " + pixelCount + " pixels" );
        int background = img.getRGB( 0, 0 );
        img.setRGB( width / 2, height / 2, color );
 
        for( int i = 0; i < pixelCount; i++ ) {
            p.x = (int) ( Math.random() * width );
            p.y = (int) ( Math.random() * height );
 
            while ( true ) {
                int dx = (int) ( Math.random() * 3 ) - 1;
                int dy = (int) ( Math.random() * 3 ) - 1;
                nextP.setLocation( p.x + dx, p.y + dy );
                // handle out-of-bounds
                if ( nextP.x < 0 || nextP.x >= width || nextP.y < 0
                        || nextP.y >= height ) {
                        // increment the number of pixels lost and escape the loop
                    pixelsLost++;
                    break;
                }
                if ( img.getRGB( nextP.x, nextP.y ) != background ) {
                    img.setRGB( p.x, p.y, color );
                    break;
                }
                p.setLocation( nextP );
            }
            // Print a message every 2%
            if ( i % ( pixelCount / 50 ) == 0 ) {
                System.out.println( "Done with " + i + " pixels" );
            }
        }
        // We're done. Let the user know how many pixels were lost
        System.out.println( "Finished. Pixels lost = " + pixelsLost );
    }
 
    public BufferedImage getImage() {
        return img;
    }
 
    public int getWidth() {
        return width;
    }
 
    public int getHeight() {
        return height;
    }
 
    public static void main( String[] args ) {
        // create the new generator
        BasicBrownianTree generator = new BasicBrownianTree( 0x664444ff, 400, 0.4 );
        // generate the image
        generator.generate();
        try {
            // save the image to the file "image.png"
            ImageIO.write( generator.getImage(), "png", new File( "image.png" ) );
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }
}

Javascript

使用canvas绘制

function brownian(canvasId, messageId) {
  var canvas = document.getElementById(canvasId);
  var ctx = canvas.getContext("2d");
 
  // Options
  var drawPos = true;
  var seedResolution = 50;
  var clearShade = 0; // 0..255
 
  // Static state
  var width = canvas.width;
  var height = canvas.height;
  var cx = width/2;
  var cy = height/2;
  var clearStyle = "rgba("+clearShade+", "+clearShade+", "+clearShade+", 1)";
 
  // Utilities
  function radius(x,y) {
    return Math.sqrt((x-cx)*(x-cy)+(y-cx)*(y-cy));
  }
  function test(x, y) {
    if (x < 0 || y < 0 || x >= width || y >= height)
      return false;
    var data = ctx.getImageData(x, y, 1, 1).data;
    return data[0] != clearShade || data[1] != clearShade || data[2] != clearShade;
  }
  var shade = 120;
  function setc(x, y, c) {
    //var imgd = ctx.createImageData(1, 1);
    //var pix = imgd.data;
    //pix[0] = pix[1] = pix[2] = c == 255 ? 255 : shade;
    //pix[3] = 255;
    //shade = (shade + 1) % 254;
    //ctx.putImageData(imgd, x, y);
    //ctx.fillStyle = "rgba("+c+", "+c+", "+c+", 1)";
    shade = (shade + 0.02) % 360;
    if (c) {
      ctx.fillStyle = "hsl("+shade+", 100%, 50%)";
    } else {
      ctx.fillStyle = clearStyle;
    }
    ctx.fillRect (x, y, 1, 1);
  }
  function set(x,y) {
    setc(x,y,true);
  }
  function clear(x,y) {
    setc(x,y,false);
  }
 
  // Initialize canvas to blank opaque
  ctx.fillStyle = clearStyle;
  ctx.fillRect (0, 0, width, height);
 
  // Current position
  var x;
  var y;
 
  // Farthest distance from center a particle has yet been placed.
  var closeRadius = 1;
 
  // Place seed
  set(cx, cy);
 
  // Choose a new random position for a particle (not necessarily unoccupied)
  function newpos() {
    // Wherever particles are injected, the tree will tend to grow faster 
    // toward it. Ideally, particles wander in from infinity; the best we
    // could do is to have them wander in from the edge of the field.
    // But in order to have the rendering occur in a reasonable time when
    // the seed is small, without too much visible bias, we instead place 
    // the particles in a coarse grid. The final tree will cover every
    // point on the grid.
    //
    // There's probably a better strategy than this.
    x = Math.floor(Math.random()*(width/seedResolution))*seedResolution;
    y = Math.floor(Math.random()*(height/seedResolution))*seedResolution;
  }
  newpos();
 
  var animation;
  animation = window.setInterval(function () {
    if (drawPos) clear(x,y);
    for (var i = 0; i < 10000; i++) {
      var ox = x;
      var oy = y;
 
      // Changing this to use only the first four directions will result
      // in a denser tree.
      switch (Math.floor(Math.random()*8)) {
        case 0: x++; break;
        case 1: x--; break;
        case 2: y++; break;
        case 3: y--; break;
        case 4: x++; y++; break;
        case 5: x--; y++; break;
        case 6: x++; y--; break;
        case 7: x--; y--; break;
      }
      if (x < 0 || y < 0 ||
          x >= width || y >= height ||
          radius(x,y) > closeRadius+seedResolution+2) {
        // wandered out of bounds or out of interesting range of the
        // tree, so pick a new spot
        var progress = 1000;
        do {
          newpos();
          progress--;
        } while ((test(x-1,y-1) || test(x,y-1) || test(x+1,y-1) ||
                  test(x-1,y  ) || test(x,y  ) || test(x+1,y  ) ||
                  test(x-1,y+1) || test(x,y+1) || test(x+1,y+1)) && progress > 0);
        if (progress <= 0) {
          document.getElementById(messageId).appendChild(
              document.createTextNode("Stopped for lack of room."));
          clearInterval(animation);
          break;
        }
      }
      if (test(x, y)) {
        // hit something, mark where we came from and pick a new spot
        set(ox,oy);
        closeRadius = Math.max(closeRadius, radius(ox,oy));
        newpos();
      }
   }
   if (drawPos) set(x,y);
  }, 1);
 
}
<html>
 <head>
  <script src="brownian.js"></script>
 </head>
 <body onload="brownian('canvas', 'message')">
   <canvas id="canvas" width="402" height="402" style="border: 2px inset;"></canvas>
   <div id="message"></div>
 </body>
</html>

Kotlin

import java.awt.Graphics
import java.awt.image.BufferedImage
import java.util.*
import javax.swing.JFrame
 
class BrownianTree : JFrame("Brownian Tree"), Runnable {
    private val img: BufferedImage 
    private val particles = LinkedList<Particle>()
 
    private companion object { 
        val rand = Random()
    } 
 
    private inner class Particle {
        private var x = rand.nextInt(img.width)
        private var y = rand.nextInt(img.height)
 
        /* returns true if either out of bounds or collided with tree */
        fun move(): Boolean {
            val dx = rand.nextInt(3) - 1
            val dy = rand.nextInt(3) - 1
            if ((x + dx < 0) || (y + dy < 0) || (y + dy >= img.height) ||
                (x + dx >= img.width)) return true
            x += dx
            y += dy
            if ((img.getRGB(x, y) and 0xff00) == 0xff00) {
                img.setRGB(x - dx, y - dy, 0xff00)
                return true
            }
            return false
        }
    } 
 
    init {
        setBounds(100, 100, 400, 300)     
        defaultCloseOperation = EXIT_ON_CLOSE
        img = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) 
        img.setRGB(img.width / 2, img.height / 2, 0xff00)
    }
 
    override fun paint(g: Graphics) {    
        g.drawImage(img, 0, 0, this)
    }
 
    override fun run() {
        (0 until 20000).forEach { particles.add(Particle()) }        
 
        while (!particles.isEmpty()) {
            val iter = particles.iterator()
            while (iter.hasNext()) {
                if (iter.next().move()) iter.remove()
            }            
            repaint()
        }
    }
}
 
fun main(args: Array<String>) {
    val b = BrownianTree()
    b.isVisible = true
    Thread(b).start()
}

Python

import pygame, sys, os
from pygame.locals import *
from random import randint
pygame.init()
 
MAXSPEED = 15
SIZE = 3
COLOR = (45, 90, 45)
WINDOWSIZE = 400
TIMETICK = 1
MAXPART = 50
 
freeParticles = pygame.sprite.Group()
tree = pygame.sprite.Group()
 
window = pygame.display.set_mode((WINDOWSIZE, WINDOWSIZE))
pygame.display.set_caption("Brownian Tree")
 
screen = pygame.display.get_surface()
 
 
class Particle(pygame.sprite.Sprite):
    def __init__(self, vector, location, surface):
        pygame.sprite.Sprite.__init__(self)
        self.vector = vector
        self.surface = surface
        self.accelerate(vector)
        self.add(freeParticles)
        self.rect = pygame.Rect(location[0], location[1], SIZE, SIZE)
        self.surface.fill(COLOR, self.rect)
 
    def onEdge(self):
        if self.rect.left <= 0:
            self.vector = (abs(self.vector[0]), self.vector[1])
        elif self.rect.top <= 0:
            self.vector = (self.vector[0], abs(self.vector[1]))
        elif self.rect.right >= WINDOWSIZE:
            self.vector = (-abs(self.vector[0]), self.vector[1])
        elif self.rect.bottom >= WINDOWSIZE:
            self.vector = (self.vector[0], -abs(self.vector[1]))
 
    def update(self):
        if freeParticles in self.groups():
            self.surface.fill((0,0,0), self.rect)
            self.remove(freeParticles)
            if pygame.sprite.spritecollideany(self, freeParticles):
                self.accelerate((randint(-MAXSPEED, MAXSPEED), 
                                 randint(-MAXSPEED, MAXSPEED)))
                self.add(freeParticles)
            elif pygame.sprite.spritecollideany(self, tree):
                self.stop()
            else:
                self.add(freeParticles)
 
            self.onEdge()
 
            if (self.vector == (0,0)) and tree not in self.groups():
                self.accelerate((randint(-MAXSPEED, MAXSPEED), 
                                 randint(-MAXSPEED, MAXSPEED)))
            self.rect.move_ip(self.vector[0], self.vector[1])
        self.surface.fill(COLOR, self.rect)
 
    def stop(self):
        self.vector = (0,0)
        self.remove(freeParticles)
        self.add(tree)
 
    def accelerate(self, vector):
        self.vector = vector
 
NEW = USEREVENT + 1
TICK = USEREVENT + 2
 
pygame.time.set_timer(NEW, 50)
pygame.time.set_timer(TICK, TIMETICK)
 
 
def input(events):
    for event in events:
        if event.type == QUIT:
            sys.exit(0)
        elif event.type == NEW and (len(freeParticles) < MAXPART):
            Particle((randint(-MAXSPEED,MAXSPEED),
                      randint(-MAXSPEED,MAXSPEED)),
                     (randint(0, WINDOWSIZE), randint(0, WINDOWSIZE)), 
                     screen)
        elif event.type == TICK:
            freeParticles.update()
 
 
half = WINDOWSIZE/2
tenth = WINDOWSIZE/10
 
root = Particle((0,0),
                (randint(half-tenth, half+tenth), 
                 randint(half-tenth, half+tenth)), screen)
root.stop()
 
while True:
    input(pygame.event.get())
    pygame.display.flip()
发布了56 篇原创文章 · 获赞 166 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_36721220/article/details/96564134