3次ベジェ曲線を加減算&乗算&ビット演算で高速に計算する

これから徐々に ES6 を目指そう!

なんと偶然なのかES6を勉強し始めました。
ほう!とかへえ!とか面白いです。
アロー関数のthisの扱いに戸惑う。forEach とかで this とかを多用している身としては残念。
しかも省略したアロー関数はiPad & iPod Toush では無理だった。

<!DOCTYPE html>
<meta charset="utf-8">
<title>3次ベジェ曲線</title>
<style>
</style>

<body>
<h1>ベジエ曲線を整数の加減算だけで描画する方法</h1>
<h2>高速に2次ベジェ曲線を計算する</h2>
<p>JavaScriptでは整数型の演算ができるわけではないが以下を参考に作る
<p><a href="http://studio-rain.cocolog-nifty.com/blog/2008/07/post_c4f5.html">
http://studio-rain.cocolog-nifty.com/blog/2008/07/post_c4f5.html</a>
<p><a href="https://peta.okechan.net/test/bezier3/">
https://peta.okechan.net/test/bezier3/</a>

<p>世の中すごい人だらけ</p>

<canvas width="800" height="400"></canvas>
<script>
//"use strict";


{// Canvas コントローラー

  const DEF_OPTION = {
    offset: {x: 0, y: 0},
    fillColor: 'rgb(0,0,0)',
    grid: {x: 30, y: 30}
  };

  const PI2 = Math.PI * 2;


  class Controller {

    constructor (canvas, option = { }) {
      this.canvas = canvas;
      this.ctx    = canvas.getContext ('2d');
      this.option = Object.assign (DEF_OPTION, option);
    }


    setGrid () {
      var [ctx, oft, cav] = [this.ctx, this.option.offset, this.canvas];
      var [x, y] = [oft.x, oft.y];
      var [w, h] = [cav.width, cav.height];

      ctx.beginPath();
      ctx.strokeStyle = 'rgb(200,200,200)';
      ctx.strokeRect (0, 0, w, h);

      ctx.fillStyle = 'rgb(150,150,150)';
      ctx.fillRect (x+1, y-1, w-2, 3);
      ctx.fillRect (x-1, 1, 3, h-2);
    }


    pset (x, y, color = this.option.fillColor, size = 1) {
      var offset = this.option.offset;
      var ctx = this.ctx;

      ctx.fillStyle = color;
      ctx.fillRect (x + offset.x, offset.y - y, size, size)
    }


    point (x, y, size = 1, color = this.option.fillColor) {
      var offset = this.option.offset;
      var ctx = this.ctx;

      ctx.fillStyle = color;
      ctx.beginPath ();
      ctx.arc(x + offset.x, offset.y - y, size, 0, PI2);
      ctx.fill ();
    }
  }

  this.CTX = Controller;
}


{
  // view-source:https://peta.okechan.net/test/bezier3/

  var nbit = 7; //=128
  var n3bit = nbit * 3;
  var N = 1 << nbit;

  function intBezier3 (x0, y0, x1, y1, x2, y2, x3, y3) {

    var NB3x = x0 << n3bit;
    var NB3y = y0 << n3bit;

    var dNB3x = x3 + (3 * N - 3) * x2 + (3 * N * N - 6 * N + 3) * x1 + (-3 * N * N + 3 * N - 1) * x0;
    var dNB3y = y3 + (3 * N - 3) * y2 + (3 * N * N - 6 * N + 3) * y1 + (-3 * N * N + 3 * N - 1) * y0;

    var d2NB3x = 6 * x3 + (6 * N - 18) * x2 + (18 - 12 * N) * x1 + (6 * N - 6) * x0;
    var d2NB3y = 6 * y3 + (6 * N - 18) * y2 + (18 - 12 * N) * y1 + (6 * N - 6) * y0;

    var d3NB3x = 6 * x3 - 18 * x2 + 18 * x1 - 6 * x0;
    var d3NB3y = 6 * y3 - 18 * y2 + 18 * y1 - 6 * y0;


    var i;
    this.push ([x0, y0]);

    for (i = 0; i < N; i++){
      NB3x += dNB3x;
      NB3y += dNB3y;
      dNB3x += d2NB3x;
      dNB3y += d2NB3y;
      d2NB3x += d3NB3x;
      d2NB3y += d3NB3y;
      this.push ([NB3x >> n3bit, NB3y >> n3bit]);
    }
  }


  function intBezier (p) {
    var a = p[0],
        b = p[1],
        c = p[2],
        d = p[3];
    var i = 4,
        r = [ ];

    while (d) {
      intBezier3.call (r, a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);

      a = d;
      b = [
        d[0] + (d[0] - c[0]),
        d[1] + (d[1] - c[1])
      ];
      c = p[i++];
      d = p[i++];
    }
    return r;
  }


  this.intBezier3 = intBezier;
}



var c = new CTX (document.querySelector ('canvas'), { offset: {x: 0, y: 255}});
var p = intBezier3 ([[0,60], [100,200],[300,60], [800,100]]);
var i, q;

c.setGrid ();
for(i = 0; q = p[i]; i+=1) {
  c.point (q[0], q[1], 4, 'rgb(255,0,0)');
}


</script>

見よう見まねで ES6で書いてみた

<!DOCTYPE html>
<meta charset="utf-8">
<title>3次ベジェ曲線</title>
<style>
</style>

<body>
<h1>ベジエ曲線を整数の加減算だけで描画する方法</h1>
<h2>高速に2次ベジェ曲線を計算する</h2>
<p>JavaScriptでは整数型の演算ができるわけではないが以下を参考に作る
<p><a href="http://studio-rain.cocolog-nifty.com/blog/2008/07/post_c4f5.html">
http://studio-rain.cocolog-nifty.com/blog/2008/07/post_c4f5.html</a>
<p><a href="https://peta.okechan.net/test/bezier3/">
https://peta.okechan.net/test/bezier3/</a>

<p>世の中すごい人だらけ</p>

<canvas width="800" height="400"></canvas>
<script>
"use strict";


{// Canvas コントローラー

  const
    DEF_OPTION = {
      offset: {x: 0, y: 0},
      fillColor: 'rgb(0,0,0)',
      strokeStyle: 'rgb(0,0,0)',
      grid: {x: 30, y: 30}
     },
    PI2 = Math.PI * 2;


  class Controller {

    constructor (canvas, option = { }) {
      this.canvas = canvas;
      this.ctx    = canvas.getContext ('2d');
      this.option = Object.assign (DEF_OPTION, option);

      if (option.grid)
        this.setGrid ();
    }


    setGrid () {
      let
        [ctx, oft, cav] = [this.ctx, this.option.offset, this.canvas],
        {x, y} = oft,
        {width, height} = cav;

      ctx.beginPath();
      ctx.strokeStyle = 'rgb(200,200,200)';
      ctx.strokeRect (0, 0, width, height);

      ctx.fillStyle = 'rgb(150,150,150)';
      ctx.fillRect (x+1, y-1, width-2, 3);
      ctx.fillRect (x-1, 1, 3, height-2);
    }


    pset (px, py, color = this.option.fillColor, size = 1) {
      let
        {x, y} = this.option.offset,
        ctx    = this.ctx;

      ctx.fillStyle = color;
      ctx.fillRect (px + x, y - py, size, size);
    }


    point (px, py, size = 1, color = this.option.fillColor) {
      let
        {x, y} = this.option.offset,
        ctx    = this.ctx;

      ctx.fillStyle = color;
      ctx.beginPath ();
      ctx.arc(px + x, y - py, size, 0, PI2);
      ctx.fill ();
    }
  }

  this.CTX = Controller;
}



{ // view-source:https://peta.okechan.net/test/bezier3/

  const int_bezier3 = function (r, x0, y0, x1, y1, x2, y2, x3, y3) {
    let
      [nBit, q, N, q0, q1, q2, q3, q4, q5] = this.pram,
      NB3x = x0 << q,
      NB3y = y0 << q,
      dNB3x = x3 + q0 * x2 + q1 * x1 + q2 * x0,
      dNB3y = y3 + q0 * y2 + q1 * y1 + q2 * y0,
      d2NB3x = 6 * x3 + q3 * x2 + q4 * x1 + q5 * x0,
      d2NB3y = 6 * y3 + q3 * y2 + q4 * y1 + q5 * y0,
      d3NB3x = 6 * x3 - 18 * x2 + 18 * x1 - 6 * x0,
      d3NB3y = 6 * y3 - 18 * y2 + 18 * y1 - 6 * y0;

    r.push ([x0, y0]);
    for (let i = N; i--; ) {
      NB3x += dNB3x;    NB3y += dNB3y;
      dNB3x += d2NB3x; 	dNB3y += d2NB3y;
      d2NB3x += d3NB3x; d2NB3y += d3NB3y;
      r.push ([NB3x >> q, NB3y >> q]);
    }
  };
    
  
  class IntBezier3 {

    constructor (nBit = 7) {
      if (nBit < 1 || 10 < nBit)
        throw new Error ('ビット数が範囲外!!');
       
      let N = 1 << nBit;
      this.pram = [
         nBit,
         nBit * 3,
         N,
         3 * N - 3,
         3 * N * N - 6 * N + 3, 
        -3 * N * N + 3 * N - 1,
         6 * N - 18,
        18 - 12 * N,
         6 * N - 6
      ];
    }
    

    getPoint (p = [ ]) {
      if (4 > p.length)
        throw new Error ('配列の数が足りません');

      let [a, b, c, d] = p.slice (0, 4), i = 4, r = [ ], f, g;

      do {
        int_bezier3.call (this, r, ...a, ...b, ...c, ...d);

        if (! (f = p[i++]) || ! (g = p[i++]))
          break; //点を2個づつ消費する。半端な場合は途中までの結果を返す

        let [dx, dy] = d, [cx, cy] = c;
        [a, c, d, ...b] = [d, f, g, dx + dx - cx, dy + dy - cy];
        //bは、前の点Cとdの直線上の点

      } while (true);

      return r;
    };
  }

  this.IntBezier3 = IntBezier3;
}

new CTX (document.querySelector ('canvas'), { offset: {x: 0, y: 255}, grid: true });


var bz = new IntBezier3 ();
var ary = bz.getPoint ([[0,60], [200,450],[400,0], [600,0]]);
    ary.forEach (
      function (q) { this.point (...q, 4, 'rgb(255,0,0)') },
      new CTX (document.querySelector ('canvas'), { offset: {x: 0, y: 255}, grid: true })
    );

</script>

ES6にむけて

(function () {
  function fuga () {
    //;
  }

  function Hoge (def) {
    this.def = def;
  }

  function piyo () {
    huga.call (this);
  }


  function create (def) {
    if ('undefined' === typeof def)
      def = 123;
    return new Hoge (def);
  }

  Hoge.prototype.piyo = piyo;
  Hoge.create = create;

  this.Hoge = Hoge;
}) ();

下のように置き換える

{
  const fuga = function () { ; };

  class Hoge {
    constructor (def = 123) {
      this.def = def;
    }

    piyo () {
      fuga.call (this);
    }
  }

  this.Hoge = Hoge;
}