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; }