[CreateJS] ドットの光 (4) ~BitmapData, Bresenham, ColorHSV~

CreateJSを使って、光るドットの表現に挑戦してみるよ。 :boy:

createjs_dotlight4

BitmaData for EaselJS を使ってみちゃうよ。 :boy:
DotLight, EmitLight, Dotクラスでつくってみたよ。 :bouzu:
Bresenham, ColorHSVクラスなんてのもつくったよ。 :cake:

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ドットの光 (3) | CreateJS</title>
<script src="js/easeljs-0.8.1.min.js"></script>
<script src="js/libs/bitmapdata-1.1.2.min.js"></script>
<script src="js/DotLight.js"></script>
<script src="js/EmitLight.js"></script>
<script src="js/Dot.js"></script>
<script src="js/Bresenham.js"></script>
<script src="js/ColorHSV.js"></script>
<script src="js/stats.min.js"></script>

<style type="text/css">
  #canvas {
  display:block;
    width: 480px;
    height: 480px;
    margin-top: 40px;
    margin-left:auto;
    margin-right:auto;
    padding-left:0;
    padding-right:0;
  }
</style>

<script>
var canvas, stage, stats;
var light;

function init() {
  stats = new Stats();
  stats.setMode(0);
  stats.domElement.style.position = "fixed";
  stats.domElement.style.right = "0px";
  stats.domElement.style.top = "0px";
  document.body.appendChild(stats.domElement);

  canvas = document.getElementById("canvas");

  stage = new createjs.Stage(canvas);

  initialize();
  background();

  stage.update();

  createjs.Ticker.setFPS(30);
  createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
  createjs.Ticker.addEventListener("tick", update);
}
function initialize() {
  light = new DotLight(new createjs.Rectangle(0, 0, 480, 480));
  stage.addChild(light);
}
function update(event) {
  light.update();
  stage.update();

  stats.update();
}
function background() {
  var title = new createjs.Text("CreateJS", "24px Myriad Pro", "#FFFFFF");
  stage.addChild(title);
  title.textAlign = "left";
  title.textBaseline = "bottom";
  title.x = 10;
  title.y = 30;
  title.alpha = 0.6;
  var version = new createjs.Text("[0.8.1]", "14px Myriad Pro", "#FFFFFF");
  stage.addChild(version);
  version.textAlign = "left";
  version.textBaseline = "bottom";
  version.x = 105;
  version.y = 26;
  version.alpha = 0.6;
  var subtitle = new createjs.Text("DotLight", "20px Myriad Pro", "#FFFFFF");
  stage.addChild(subtitle);
  subtitle.textAlign = "left";
  subtitle.textBaseline = "bottom";
  subtitle.x = 160;
  subtitle.y = 28;
  subtitle.alpha = 0.6;
  var publish = new createjs.Text("authoring: Sublime Text 2 + Flash CC", "14px Myriad Pro", "#FFFFFF");
  stage.addChild(publish);
  publish.textAlign = "right";
  publish.textBaseline = "bottom";
  publish.x = canvas.width - 10;
  publish.y = 478;
  publish.alpha = 0.6;
}
</script>
</head>
<body onload="init();" style="background-color:#333333">
  <canvas id="canvas" width="480" height="480" style="background-color:#000000"></canvas>
</body>
</html>

DotLight.js
(function() {

function DotLight(rect) {
  this.Container_constructor();

  this.init(rect);
}
var p = createjs.extend(DotLight, createjs.Container);

var scale = 4;

p.rect;
p.bitmapData;
p.map;
p.offsets = [];
p.seed;
p.light;
p.time = 0;

p.init = function(rect) {
  this.rect = rect;

  this.bitmapData = new createjs.BitmapData(null, this.rect.width, this.rect.height, 0x00000000);
  var bitmap = new createjs.Bitmap(this.bitmapData.canvas);
  this.addChild(bitmap);

  this.map = new createjs.BitmapData(null, this.rect.width/scale, this.rect.height/scale, 0xFF000000);
  this.offsets = [new createjs.Point(), new createjs.Point()];
  this.seed = Math.random()*1000 >> 0;

  this.light = new EmitLight(this.bitmapData, this.map, scale);
};
p.update = function() {
  this.time += 16 + Math.random()*16;

  var offset = this.time*0.05;
  this.offsets[0].x = this.offsets[1].y = offset;
  this.map.perlinNoise(this.rect.width/scale, this.rect.height/scale, 2, this.seed, false, true, 3, false, this.offsets);

  this.light.create(10, this.time);
  this.bitmapData.fillRect(this.rect, 0x00000000);
  this.light.emit();
  this.bitmapData.updateContext();
};

window.DotLight = createjs.promote(DotLight, "Container");

}());

EmitLight.js
(function() {

function EmitLight(bd, m, s) {
  this.init(bd, m, s);
}
var p = EmitLight.prototype;

var speed = 1.2;
var offset = 0.4;
var deceleration = 0.01;
var length = 2;

p.bitmapData;
p.map;
p.rect;
p.cx;
p.cy;
p.scale;
p.dots = [];
p.manager;

p.init = function(bd, m, s) {
  this.bitmapData = bd;
  this.map = m;
  this.rect = bd.rect;
  this.cx = this.rect.width/2;
  this.cy = this.rect.height/2;
  this.scale = s;
  this.manager = new Bresenham(this.bitmapData);
};
p.create = function(max, time) {
  for (var n = 0; n < max; n++) {
    var angle = Math.random()*Math.PI;
    var dx = Math.cos(angle);
    var dy = Math.sin(angle);
    var px = this.cx + dx*5;
    var py = this.cy + dy*5;
    var dot = new Dot(px, py, angle);
    dot.vx = dx;
    dot.vy = dy;
    var hue = time/10000;
    color = new ColorHSV(hue, 0.5, 1);
    dot.rgb = color.value();
    this.dots.push(dot);
  }
};
p.emit = function() {
  var max = this.dots.length;
  for (var n = 0; n < max; n++) {
    var dot = this.dots[n];
    if (!dot) continue;
    var c = this.map.getPixel(dot.x/this.scale >> 0, dot.y/this.scale >> 0);
    dot.vx += (((c >> 16) & 0xFF)/0xFF - offset)*speed;
    dot.vy += (((c >> 8) & 0xFF)/0xFF - offset)*speed;
    dot.x += dot.vx;
    dot.y += dot.vy;
    dot.energy -= deceleration;
    var x0 = dot.x;
    var y0 = dot.y;
    var x1 = dot.x - (dot.x - dot.px)*length;
    var y1 = dot.y - (dot.y - dot.py)*length;
    this.manager.draw(x0, y0, x1, y1, dot.rgb, dot.energy*0.5);
    dot.px = dot.x;
    dot.py = dot.y;
    if (dot.energy < 0 || dot.x < this.rect.left || dot.x > this.rect.right || dot.y < this.rect.top || dot.y > this.rect.bottom) {
      this.dots.splice(n, 1);
      dot = null;
    }
  }
};

window.EmitLight = EmitLight;

}());

Dot.js
(function() {

function Dot(x, y, a) {
  this.init(x, y, a);
}
var p = Dot.prototype;

p.x = 0;
p.y = 0;
p.px = 0;
p.py = 0;
p.angle = 0;
p.vx = 0;
p.vy = 0;
p.energy = 2;
p.rgb = 0xFFFFFF;

p.init = function(x, y, a) {
  this.x = x;
  this.y = y;
  this.px = x;
  this.py = y;
  this.angle = a;
};

window.Dot = Dot;

}());

Bresenham.js
(function() {

function Bresenham(bd) {
  this.init(bd);
}
var p = Bresenham.prototype;

p.bitmapData;

p.init = function(bd) {
  this.bitmapData = bd;
};
p.draw = function(x0, y0, x1, y1, color, alpha) {
  var steep = Math.abs(y1 - y0) > Math.abs(x1 - x0);
  var t;
  if (steep) {
    t = x0;
    x0 = y0;
    y0 = t;
    t = x1;
    x1 = y1;
    y1 = t;
  }
  if (x0 > x1) {
    t = x0;
    x0 = x1;
    x1 = t;
    t = y0;
    y0 = y1;
    y1 = t;
  }
  var dx = x1 - x0;
  var dy = Math.abs(y1 - y0);
  var e = dx*0.5;
  var ys = (y0 < y1) ? 1 : -1;
  var y = y0;
  for (var x = x0; x <= x1; x ++ ) {
    if (steep) {
      this.plot(y, x, color, alpha);
    } else {
      this.plot(x, y, color, alpha);
    }
    e = e - dy;
    if (e < 0) {
      y = y + ys;
      e = e + dx;
    }
  }
};
p.plot = function(x, y, color, alpha) {
  this.bitmapData.setPixel32(x, y, color | ((alpha*0xFF) << 24));
};

window.Bresenham = Bresenham;

}());

ColorHSV.js
(function() {

function ColorHSV(h, s, v) {
  this.init(h, s, v);
}
var p = ColorHSV.prototype;

p.rgb = 0xFFFFFF;

p.init = function(h, s, v) {
  var r, g, b, i, f, p, q, t;
  if (h && s === undefined && v === undefined) {
    s = h.s, v = h.v, h = h.h;
  }
  i = (h * 6) >> 0;
  f = h * 6 - i;
  p = v * (1 - s);
  q = v * (1 - f * s);
  t = v * (1 - (1 - f) * s);
  switch (i % 6) {
    case 0: r = v, g = t, b = p; break;
    case 1: r = q, g = v, b = p; break;
    case 2: r = p, g = v, b = t; break;
    case 3: r = p, g = q, b = v; break;
    case 4: r = t, g = p, b = v; break;
    case 5: r = v, g = p, b = q; break;
  }
  this.rgb = (((r*0xFF) << 16) >> 0) | (((g*0xFF) << 8) >> 0) | ((b*0xFF) >> 0);
};
p.value = function() {
  return this.rgb;
};

window.ColorHSV = ColorHSV;

}());


参考資料「BitmapData for EaselJS」(リファレンス)


[修正] (15/09/21 Mon 01:00)
CreateJS 0.8.1 にバージョンアップ。