LoginSignup
9
8

More than 5 years have passed since last update.

「プログラムでシダを描画する」を CoffeeScript (HTML5 Canvas) で描画する

Last updated at Posted at 2014-12-17

「CoffeeScript でシダを描画する」をしました。。。これって、半年くらい前にブームだったんですね。
既にブームは去っているようですが、気にせず投稿。

shida.png

※ブラウザ: Chromium バージョン 39.0.2171.65 Ubuntu 14.04 (64-bit)


JavaScript は CoffeeScript で記述。jQuery を使用。Canvas に描画。

myscript.coffee
$ ->
  # 乱数
  rand = (max = 0xff, min = 0) -> min + Math.random() * (max - min + 1) | 0  

  #
  # ---- Canvas ---------------------------------------------
  #
  plotter = (color = (() -> [0, 0xff, 0, 0xff]), canvas = $('canvas')[0]) ->
    ctx = canvas.getContext '2d'
    img = ctx.createImageData canvas.width, canvas.height

    put_pixel = (x, y, rgba) ->
      offset = (x * 4) + (y * canvas.width * 4)
      rgba.forEach (c, i) -> img.data[offset + i] = c

    put: (xy, rgba = color()) ->
      x = xy[0] * (+1) * (canvas.width  - 10) + (canvas.width / 2) |0
      y = xy[1] * (-1) * (canvas.height - 10) + (canvas.height   ) |0
      put_pixel x, y, rgba
      this

    plot: (g) ->
      g.apply null, [this.put].concat([].slice.call arguments, 1)
      this

    clear: (rgba = [0,0,0,0]) ->
      [0...canvas.width].forEach (x) ->
        [0...canvas.height].forEach (y) -> put_pixel x, y, rgba
      this

    show: () ->
      ctx.putImageData img, 0, 0
      this

  #
  # ---- 「シダ」のアルゴリズム ----------------------------
  #
  fern = (callback = (() ->), k = 20) ->
    w1x = (x, y) ->  0.836 * x + 0.044 * y
    w1y = (x, y) -> -0.044 * x + 0.836 * y + 0.169
    w2x = (x, y) -> -0.141 * x + 0.302 * y
    w2y = (x, y) ->  0.302 * x + 0.141 * y + 0.127
    w3x = (x, y) ->  0.141 * x - 0.302 * y
    w3y = (x, y) ->  0.302 * x + 0.141 * y + 0.169
    w4x = (x, y) ->  0.0
    w4y = (x, y) ->  0.175337 * y

    f = (k, x, y) ->
      if 0 < k
        f(k - 1, w1x(x, y), w1y(x, y))
        f(k - 1, w2x(x, y), w2y(x, y)) if Math.random() < 0.3
        f(k - 1, w3x(x, y), w3y(x, y)) if Math.random() < 0.3
        f(k - 1, w4x(x, y), w4y(x, y)) if Math.random() < 0.3
      else 
        callback [x, y]

    f(k, 0, 0)

  #
  # ---- 描画の実行 -----------------------------------------
  #
  console.log '描画開始'

  color = () -> [rand()/4, rand(), rand()/2, 0xff - rand()/8]

  plotter color     # ここを plotter() にすると緑単色(デフォルト)になる
    .clear()
    .plot fern
    .show()

  console.log '描画終了'

HTML は Haml で記述。

sample.haml
!!!
%meta(charset="UTF-8")
%title #{@title="CoffeeScript でシダを描画する"}

%h1 #{@title}
%canvas(width="500" height="500" style="background-color: #000000;")
  このブラウザは HTML5 Canvas に対応していません。

%script(src="//code.jquery.com/jquery-1.11.1.min.js")
%script(src="myscript.js")

Haml -> HTML、CoffeeScript -> JavaScript への変換。

$ haml -q sample.haml sample.html   # 'sample.html' が生成される
$ coffee -c myscript.coffee         # 'myscript.js' が生成される

変換後の HTML と JavaScript

sample.html
<!DOCTYPE html>
<meta charset="UTF-8">
<title>CoffeeScript でシダを描画する</title>
<h1>CoffeeScript でシダを描画する</h1>
<canvas height="500" style="background-color: #000000;" width="500">
  このブラウザは HTML5 Canvas に対応していません。
</canvas>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="myscript.js"></script>
myscript.js
// Generated by CoffeeScript 1.8.0
$(function() {
  var color, fern, plotter, rand;
  rand = function(max, min) {
    if (max == null) {
      max = 0xff;
    }
    if (min == null) {
      min = 0;
    }
    return min + Math.random() * (max - min + 1) | 0;
  };
  plotter = function(color, canvas) {
    var ctx, img, put_pixel;
    if (color == null) {
      color = (function() {
        return [0, 0xff, 0, 0xff];
      });
    }
    if (canvas == null) {
      canvas = $('canvas')[0];
    }
    ctx = canvas.getContext('2d');
    img = ctx.createImageData(canvas.width, canvas.height);
    put_pixel = function(x, y, rgba) {
      var offset;
      offset = (x * 4) + (y * canvas.width * 4);
      return rgba.forEach(function(c, i) {
        return img.data[offset + i] = c;
      });
    };
    return {
      put: function(xy, rgba) {
        var x, y;
        if (rgba == null) {
          rgba = color();
        }
        x = xy[0] * (+1) * (canvas.width - 10) + (canvas.width / 2) | 0;
        y = xy[1] * (-1) * (canvas.height - 10) + canvas.height | 0;
        put_pixel(x, y, rgba);
        return this;
      },
      plot: function(g) {
        g.apply(null, [this.put].concat([].slice.call(arguments, 1)));
        return this;
      },
      clear: function(rgba) {
        var _i, _ref, _results;
        if (rgba == null) {
          rgba = [0, 0, 0, 0];
        }
        (function() {
          _results = [];
          for (var _i = 0, _ref = canvas.width; 0 <= _ref ? _i < _ref : _i > _ref; 0 <= _ref ? _i++ : _i--){ _results.push(_i); }
          return _results;
        }).apply(this).forEach(function(x) {
          var _i, _ref, _results;
          return (function() {
            _results = [];
            for (var _i = 0, _ref = canvas.height; 0 <= _ref ? _i < _ref : _i > _ref; 0 <= _ref ? _i++ : _i--){ _results.push(_i); }
            return _results;
          }).apply(this).forEach(function(y) {
            return put_pixel(x, y, rgba);
          });
        });
        return this;
      },
      show: function() {
        ctx.putImageData(img, 0, 0);
        return this;
      }
    };
  };
  fern = function(callback, k) {
    var f, w1x, w1y, w2x, w2y, w3x, w3y, w4x, w4y;
    if (callback == null) {
      callback = (function() {});
    }
    if (k == null) {
      k = 20;
    }
    w1x = function(x, y) {
      return 0.836 * x + 0.044 * y;
    };
    w1y = function(x, y) {
      return -0.044 * x + 0.836 * y + 0.169;
    };
    w2x = function(x, y) {
      return -0.141 * x + 0.302 * y;
    };
    w2y = function(x, y) {
      return 0.302 * x + 0.141 * y + 0.127;
    };
    w3x = function(x, y) {
      return 0.141 * x - 0.302 * y;
    };
    w3y = function(x, y) {
      return 0.302 * x + 0.141 * y + 0.169;
    };
    w4x = function(x, y) {
      return 0.0;
    };
    w4y = function(x, y) {
      return 0.175337 * y;
    };
    f = function(k, x, y) {
      if (0 < k) {
        f(k - 1, w1x(x, y), w1y(x, y));
        if (Math.random() < 0.3) {
          f(k - 1, w2x(x, y), w2y(x, y));
        }
        if (Math.random() < 0.3) {
          f(k - 1, w3x(x, y), w3y(x, y));
        }
        if (Math.random() < 0.3) {
          return f(k - 1, w4x(x, y), w4y(x, y));
        }
      } else {
        return callback([x, y]);
      }
    };
    return f(k, 0, 0);
  };
  console.log('描画開始');
  color = function() {
    return [rand() / 4, rand(), rand() / 2, 0xff - rand() / 8];
  };
  plotter(color).clear().plot(fern).show();
  return console.log('描画終了');
});

確認したブラウザ

  • Chromium バージョン 39.0.2171.65 Ubuntu 14.04 (64-bit) [Ubuntu Linux 14.04]
  • Firefox 34.0 Mozilla Firefox for Ubuntu canonical - 1.0 [Ubuntu Linux 14.04]
  • Google Chrome バージョン 39.0.2171.95 (64-bit) [Mac OS X 10.10.1 Yosemite]
  • Safari バージョン 8.0.2 (10600.2.5) [Mac OS X 10.10.1 Yosemite]
  • Safari [iOS 8.1.2]
9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8