パーフェクトシャッフル
http://d.hatena.ne.jp/nishiohirokazu/20100107/1262835414
↑が奇麗すぎるので、できないかと思ってやってみた。
が、どうしても円形にする方法がわからない。
しかたがないので5角形で止まっているところまで。
実は時系列的には上の記事で書いたベジェが後になる。
今度は2次ベジェ曲線を使ってやってみる予定。
var ceil = Math.ceil; var round = Math.round; var cos = Math.cos; var sin = Math.sin; var tan = Math.tan; var pow = Math.pow; var sqrt = Math.sqrt; var pi = Math.PI; var pi_3 = Math.PI/3; function perfectshuffle(arr){ var narr = []; var m = ceil(arr.length/2); var a1 = arr.slice(0,m); var a2 = arr.slice(m); for(var i=0;0<a1.length || 0<a2.length;i++){ var t = (i & 1) == 0 ? a2 : a1; if(t.length < 1){continue} narr.push(t.shift()); } return narr; } function colorgenerate(n){ var color = []; for(var i=0;i<n;i++){ var t = 5/3 * pi * i / n; var rgb = [ 255 * (cos(t + 0 * pi_3) + 0.5), 255 * (cos(t + 4 * pi_3) + 0.5), 255 * (cos(t + 2 * pi_3) + 0.5), ]; for(var c=0;c<rgb.length;c++){ if(rgb[c] < 0){rgb[c] = 0} if(255 < rgb[c]){rgb[c] = 255} rgb[c] = round(rgb[c]) } color[i] = "rgba("+rgb.join(',')+",255)"; } return color; } function initialize(){ var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.fillRect(0,0,400,400); var drawline = function(p1,p2,color){ ctx.beginPath(); console.log(color); ctx.strokeStyle = color || 'red'; ctx.moveTo(p1[0],p1[1]); ctx.lineTo(p2[0],p2[1]); ctx.moveTo(p1[0],p1[1]); ctx.closePath(); ctx.stroke(); }; var drawcurve = function(p1,cp,p2,color){ ctx.beginPath(); ctx.strokeStyle = color; ctx.moveTo(p1[0],p1[1]); ctx.quadraticCurveTo(cp[0],cp[1],p2[0],p2[1]); ctx.moveTo(p1[0],p1[1]); ctx.closePath(); ctx.stroke(); }; var cnt = 5; var d = 6; var center = [200,200]; var target = []; var next; var cards = 30; var color = colorgenerate(cards); for(var i=0;i<cards;i++){target[i] = i} for(var j=0;j<cnt;j++){ next = perfectshuffle(target); for(var i=0;i<target.length;i++){ var t = 0; for(;t<next.length;t++){ if(next[t] == target[i]){ break; } } var r1,r2; r1 = d * (i+0.5); r2 = d * (t+0.5); drawline( [r1 * cos(2 * pi * j / cnt) + center[0],r1 * -sin(2 * pi * j / cnt) + center[1]], [r2 * cos(2 * pi * (j+1) / cnt) + center[0],r2 * -sin(2 * pi * (j+1) / cnt) + center[1]], color[target[i]] ); } target = next; } } window.addEventListener('load',initialize,false);
■注意点
- 2次ベジェ曲線の制御点で使用されている1.4は表示されたグラフィックを見ながら調整したものである。
- 色生成の引数の透過度0.03、及びimagedataの明さ調整の為の乗数24はグラフィックを身ながら調整したものである。
var ceil = Math.ceil; var round = Math.round; var cos = Math.cos; var sin = Math.sin; var tan = Math.tan; var max = Math.max; var pow = Math.pow; var sqrt = Math.sqrt; var pi = Math.PI; var pi_3 = Math.PI/3; function perfectshuffle(arr){ var narr = []; var m = ceil(arr.length/2); var a1 = arr.slice(0,m); var a2 = arr.slice(m); for(var i=0;0<a1.length || 0<a2.length;i++){ var t = (i & 1) == 0 ? a2 : a1; if(t.length < 1){continue} narr.push(t.shift()); } return narr; } function colorgenerate(n,opacity){ var color = []; if(opacity == undefined){ opacity = 1.0; } for(var i=0;i<n;i++){ var t = 5/3 * pi * i / n; var rgb = [ 255 * (cos(t + 0 * pi_3) + 0.5), 255 * (cos(t + 4 * pi_3) + 0.5), 255 * (cos(t + 2 * pi_3) + 0.5), ]; for(var c=0;c<rgb.length;c++){ if(rgb[c] < 0){rgb[c] = 0} if(255 < rgb[c]){rgb[c] = 255} rgb[c] = round(rgb[c]) } color[i] = "rgba("+rgb.join(',')+","+opacity+")"; } return color; } function initialize(){ var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); ctx.fillRect(0,0,430,430); var cnt = 5; var d = 6; var center = [215,215]; var target = []; var next; var linewidth = 4; var cards = 30; var color = colorgenerate(cards,0.03); for(var i=0;i<cards;i++){target[i] = i} var r1,r2,r3; var list = []; var next = target; for(var i=0;i<5;i++){ list.push(next) next=perfectshuffle(next); } var m = 0; for(var i=0;i<target.length;i++){ m = i; ctx.beginPath(); for(var t=0;t<list.length;t++){ var next = t+1 < cnt ? list[t+1] : list[0]; var n = 0; for(;n<next.length;n++){ if(list[t][m] == next[n]){ break; } } r1 = d * (m+0.5) + 20; r2 = d * (n+0.5) + 20; r3 = d * max(m,n) + 1.4 * max(m,n) + 20; ctx.strokeStyle = color[i]; ctx.lineWidth = 4; if(t == 0){ ctx.moveTo(r1 * cos(2 * pi * t / cnt) + center[0],r1 * -sin(2 * pi * t / cnt) + center[1]); } ctx.quadraticCurveTo( r3 * cos(2 * pi * (t+0.5) / cnt) + center[0],r3 * -sin(2 * pi * (t+0.5) / cnt) + center[1], r2 * cos(2 * pi * (t+1) / cnt) + center[0],r2 * -sin(2 * pi * (t+1) / cnt) + center[1] ) m = n; } ctx.closePath(); ctx.stroke(); } // [参考] http://nullpo.s101.xrea.com/gomi/canvas_filter_test.js var imagedata = ctx.getImageData(0,0,430,430); var br = 24; for(var i=0;i<imagedata.data.length;i+=4){ var r = imagedata.data[i]; var g = imagedata.data[i+1]; var b = imagedata.data[i+2]; var a = imagedata.data[i+3]; r *= br; g *= br; b *= br; if(255 < r){r = 255};if(255 < g){g = 255};if(255 < b){b = 255} imagedata.data[i] = r; imagedata.data[i+1] = g; imagedata.data[i+2] = b; } ctx.putImageData(imagedata,0,0); } window.addEventListener('load',initialize,false);
canvasのピクセル単位で処理を行う為に、getImageDataやputImageDataを使ってみた。
getImageDataメソッドで取得した CanvasPixelArrayは4つで1ピクセルの情報を扱う。
[参考]
- http://www.html5.jp/canvas/ref/method/getImageData.html
- http://d.hatena.ne.jp/electrolysis/20080117/1200547427
50枚のカードを8回シャッフルするバージョンはこちら