Barnsley fern

Revision as of 20:31, 29 May 2016 by rosettacode>Fwend (no longer draft)

A Barnsley fern is a fractal named after British mathematician Michael Barnsley and can be created using an iterated function system (IFS).

Task
Barnsley fern
You are encouraged to solve this task according to the task description, using any language you may know.


The task: create this fractal fern, using the following transformations:

  • ƒ1 (chosen 1% of the time)
   xn + 1 = 0
   yn + 1 = 0.16 yn
  • ƒ2 (chosen 85% of the time)
   xn + 1 = 0.85 xn + 0.04 yn
   yn + 1 = −0.04 xn + 0.85 yn + 1.6
  • ƒ3 (chosen 7% of the time)
   xn + 1 = 0.2 xn − 0.26 yn
   yn + 1 = 0.23 xn + 0.22 yn + 1.6
  • ƒ4 (chosen 7% of the time)
   xn + 1 = −0.15 xn + 0.28 yn
   yn + 1 = 0.26 xn + 0.24 yn + 0.44.

Starting position: x = 0, y = 0


C++

 

<lang cpp>

  1. include <windows.h>
  2. include <ctime>
  3. include <string>

const int BMP_SIZE = 600, ITERATIONS = static_cast<int>( 15e5 );

class myBitmap { public:

   myBitmap() : pen( NULL ), brush( NULL ), clr( 0 ), wid( 1 ) {}
   ~myBitmap() {
       DeleteObject( pen ); DeleteObject( brush );
       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( BYTE clr = 0 ) {
       memset( pBits, clr, width * height * sizeof( DWORD ) );
   }
   void setBrushColor( DWORD bClr ) {
       if( brush ) DeleteObject( brush );
       brush = CreateSolidBrush( bClr );
       SelectObject( hdc, brush );
   }
   void setPenColor( DWORD c ) {
       clr = c; createPen();
   }
   void setPenWidth( int w ) {
       wid = w; createPen();
   }
   void saveBitmap( std::string path ) {
       BITMAPFILEHEADER fileheader;
       BITMAPINFO       infoheader;
       BITMAP           bitmap;
       DWORD            wb;
       GetObject( bmp, sizeof( bitmap ), &bitmap );
       DWORD* 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 );
       HANDLE 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() const     { return hdc; }
   int getWidth() const  { return width; }
   int getHeight() const { return height; }

private:

   void createPen() {
       if( pen ) DeleteObject( pen );
       pen = CreatePen( PS_SOLID, wid, clr );
       SelectObject( hdc, pen );
   }
   HBITMAP bmp; HDC    hdc;
   HPEN    pen; HBRUSH brush;
   void    *pBits; int    width, height, wid;
   DWORD    clr;

}; class fern { public:

   void draw() {
       bmp.create( BMP_SIZE, BMP_SIZE );
       float x = 0, y = 0; HDC dc = bmp.getDC();
       int hs = BMP_SIZE >> 1;
       for( int f = 0; f < ITERATIONS; f++ ) {
           SetPixel( dc, hs + static_cast<int>( x * 55.f ), 
                     BMP_SIZE - 15 - static_cast<int>( y * 55.f ), 
                     RGB( static_cast<int>( rnd() * 80.f ) + 20, 
                          static_cast<int>( rnd() * 128.f ) + 128, 
                          static_cast<int>( rnd() * 80.f ) + 30 ) ); 
           getXY( x, y );
       }
       bmp.saveBitmap( "./bf.bmp" );
   }

private:

   void getXY( float& x, float& y ) {
       float g, xl, yl;
       g = rnd();
       if( g < .01f ) { xl = 0; yl = .16f * y; } 
       else if( g < .07f ) {
           xl = .2f * x - .26f * y;
           yl = .23f * x + .22f * y + 1.6f;
       } else if( g < .14f ) {
           xl = -.15f * x + .28f * y;
           yl = .26f * x + .24f * y + .44f;
       } else {
           xl = .85f * x + .04f * y;
           yl = -.04f * x + .85f * y + 1.6f;
       }
       x = xl; y = yl;
   }
   float rnd() {
       return static_cast<float>( rand() ) / static_cast<float>( RAND_MAX );
   }
   myBitmap bmp;

}; int main( int argc, char* argv[]) {

   srand( static_cast<unsigned>( time( 0 ) ) );
   fern f; f.draw(); return 0;    

} </lang>

J

 

<lang j>require 'plot'

f=: |: 0 ". ];._2 noun define

 0     0     0    0.16   0 0      0.01
 0.85 -0.04  0.04 0.85   0 1.60   0.85
 0.20  0.23 -0.26 0.22   0 1.60   0.07
-0.15  0.26  0.28 0.24   0 0.44   0.07

)

fm=: {&(|: 2 2 $ f) fa=: {&(|: 4 5 { f) prob=: (+/\ 6 { f) I. ?@0:

ifs=: (fa@] + fm@] +/ .* [) prob getPoints=: ifs^:(<200000) plotFern=: 'dot;grids 0 0;tics 0 0;labels 0 0;color green' plot ;/@|:

  plotFern getPoints 0 0</lang>

Java

 
Works with: Java version 8

<lang java>import java.awt.*; import java.awt.image.BufferedImage; import javax.swing.*;

public class BarnsleyFern extends JPanel {

   BufferedImage img;
   public BarnsleyFern() {
       final int dim = 640;
       setPreferredSize(new Dimension(dim, dim));
       setBackground(Color.white);
       img = new BufferedImage(dim, dim, BufferedImage.TYPE_INT_ARGB);
       createFern(dim, dim);
   }
   void createFern(int w, int h) {
       double x = 0;
       double y = 0;
       for (int i = 0; i < 200_000; i++) {
           double tmpx, tmpy;
           double r = Math.random();
           if (r <= 0.01) {
               tmpx = 0;
               tmpy = 0.16 * y;
           } else if (r <= 0.08) {
               tmpx = 0.2 * x - 0.26 * y;
               tmpy = 0.23 * x + 0.22 * y + 1.6;
           } else if (r <= 0.15) {
               tmpx = -0.15 * x + 0.28 * y;
               tmpy = 0.26 * x + 0.24 * y + 0.44;
           } else {
               tmpx = 0.85 * x + 0.04 * y;
               tmpy = -0.04 * x + 0.85 * y + 1.6;
           }
           x = tmpx;
           y = tmpy;
           img.setRGB((int) Math.round(w / 2 + x * w / 11),
                   (int) Math.round(h - y * h / 11), 0xFF32CD32);
       }
   }
   @Override
   public void paintComponent(Graphics gg) {
       super.paintComponent(gg);
       Graphics2D g = (Graphics2D) gg;
       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
       g.drawImage(img, 0, 0, null);
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(() -> {
           JFrame f = new JFrame();
           f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           f.setTitle("Barnsley Fern");
           f.setResizable(false);
           f.add(new BarnsleyFern(), BorderLayout.CENTER);
           f.pack();
           f.setLocationRelativeTo(null);
           f.setVisible(true);
       });
   }

}</lang>

Perl

 

<lang perl>use Imager;

my $w = 640; my $h = 640;

my $img = Imager->new(xsize => $w, ysize => $h, channels => 3); my $green = Imager::Color->new('#00FF00');

my ($x, $y) = (0, 0);

foreach (1 .. 2e5) {

 my $r = rand(100);
 ($x, $y) = do {
   if    ($r <=  1) { ( 0.00 * $x - 0.00 * $y,  0.00 * $x + 0.16 * $y + 0.00) }
   elsif ($r <=  8) { ( 0.20 * $x - 0.26 * $y,  0.23 * $x + 0.22 * $y + 1.60) }
   elsif ($r <= 15) { (-0.15 * $x + 0.28 * $y,  0.26 * $x + 0.24 * $y + 0.44) }
   else             { ( 0.85 * $x + 0.04 * $y, -0.04 * $x + 0.85 * $y + 1.60) }
 };
 $img->setpixel(x => $w / 2 + $x * 60, y => $y * 60, color => $green);

}

$img->flip(dir => 'v'); $img->write(file => 'barnsleyFern.png');</lang>

Perl 6

Works with: Rakudo version 2016.03
Translation of: Perl
 

<lang perl6>use Image::PNG::Portable;

my ($w, $h) = (640, 640);

my $png = Image::PNG::Portable.new: :width($w), :height($h);

my ($x, $y) = (0, 0);

for ^2e5 {

   my $r = 100.rand;
   ($x, $y) = do given $r {
       when  $r <=  1 { (                     0,              0.16 * $y       ) }
       when  $r <=  8 { ( 0.20 * $x - 0.26 * $y,  0.23 * $x + 0.22 * $y + 1.60) }
       when  $r <= 15 { (-0.15 * $x + 0.28 * $y,  0.26 * $x + 0.24 * $y + 0.44) }
       default        { ( 0.85 * $x + 0.04 * $y, -0.04 * $x + 0.85 * $y + 1.60) }
   };
   $png.set(($w / 2 + $x * 60).Int, $h - ($y * 60).Int, 0, 255, 0);

}

$png.write: 'Barnsley-fern-perl6.png';</lang>

Processing

<lang java>void setup() {

 size(640, 640);
 background(0, 0, 0);

}

float x = 0; float y = 0;

void draw() {

 for (int i = 0; i < 100000; i++) {
   float xt = 0;
   float yt = 0;
   float r = random(100);
   if (r <= 1) {
     xt = 0;
     yt = 0.16*y;
   } else if (r <= 8) {
     xt = 0.20*x - 0.26*y;
     yt = 0.23*x + 0.22*y + 1.60;
   } else if (r <= 15) {
     xt = -0.15*x + 0.28*y;
     yt =  0.26*x + 0.24*y + 0.44;
   } else {
     xt =  0.85*x + 0.04*y;
     yt = -0.04*x + 0.85*y + 1.60;
   }
   x = xt;
   y = yt;
   int m = round(width/2 + 60*x);
   int n = height-round(60*y);
   set(m, n, #00ff00);
 }
 noLoop();

}</lang>

Sidef

<lang ruby>require('Imager')

var w = 640 var h = 640

var img = %s<Imager>.new(xsize => w, ysize => h, channels => 3) var green = %s<Imager::Color>.new('#00FF00')

var (x, y) = (0, 0)

for r in (^1e5 -> lazy.map { 100.rand }) {

 (x, y) = (
   if    (r <=  1) { ( 0.00*x - 0.00*y,  0.00*x + 0.16*y + 0.00) }
   elsif (r <=  8) { ( 0.20*x - 0.26*y,  0.23*x + 0.22*y + 1.60) }
   elsif (r <= 15) { (-0.15*x + 0.28*y,  0.26*x + 0.24*y + 0.44) }
   else            { ( 0.85*x + 0.04*y, -0.04*x + 0.85*y + 1.60) }
 )
 img.setpixel(x => w/2 + 60*x, y => 60*y, color => green)

}

img.flip(dir => 'v') img.write(file => 'barnsleyFern.png')</lang>

tcl

See Tcl-Wiki [_ttp://wiki.tcl.tk/10492 Fern Fractal]

zkl

 

Uses the PPM class from http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#zkl

Translation of: Java

<lang zkl>fcn barnsleyFern(){

  w,h:=640,640;
  bitmap:=PPM(w+1,h+1,0xFF|FF|FF);  // White background
  x,y, nx,ny:=0.0, 0.0, 0.0, 0.0;
  do(0d100_000){
     r:=(0).random(100);  // [0..100)%
     if     (r<= 1) nx,ny= 0, 		      0.16*y;
     else if(r<= 8) nx,ny= 0.2*x  - 0.26*y,  0.23*x + 0.22*y + 1.6;
     else if(r<=15) nx,ny=-0.15*x + 0.28*y,  0.26*x + 0.24*y + 0.44;
     else           nx,ny= 0.85*x + 0.04*y, -0.04*x + 0.85*y + 1.6;
     x,y=nx,ny;
     bitmap[w/2 + x*60, y*60] = 0x00|FF|00;  // Green dot
  }
  bitmap.writeJPGFile("barnsleyFern.jpg");

}();</lang>