CodeIQ|ITエンジニアのための実務スキル評価サービスで挑戦した問題、川頭 信之さん (@nkawagashira)、からの挑戦者求む!【言語不問】再帰の海に棲むドラゴンを描いてみよう by @nkawagashira 川頭 信之│CodeIQ
C曲線の宮殿を旅して、ドラゴンを探し出そう!
の解答の解説、09再帰の海に棲むドラゴンを描いてみよう【問題と解説】 - スピード冒険野郎のブログ
[CodeIQ]再帰の海に棲むドラゴンを描いてみよう【問題と解説】 http://t.co/zqCU38zwsG
— 川頭信之 空飛ぶデータサイエンティスト (@nkawagashira) 2013, 12月 10
【解説】再帰の海に住むドラゴンを描いてみよう
今回は再帰関数を用いてC曲線とドラゴン曲線を描く問題です。
準備問題は言語やOS等によって異なりますので、個々に対応してください。
タートルグラフィックスは直前の描画終了点を記憶し、描画の長さlengthと描画の方向角度thetaを入力変数として描画を行うシステムです。
が出てたので、挑戦した時を思い出しながら書いてみた。
Google Chrome、Safari(特にChrome)の場合、strokeをその都度に変更して、setTimeoutを書き加えたら、一気に描画されるのではなく、再帰関数(左側から)で描画されるていく様子(左側から)が分かるようになった。strokeを毎回呼び出すようにしだけでは、徐々に表示されるようにはならなかった。
png形式のイメージを取得については、Firefoxはcanvasを右クリックで直接png形式の画像として保存することができた。Chrome、Safariでpng形式の画像を取得するには変換する必要があった。
徐々に描かれていくのと、一度で描く、2つのバージョンを書いてみた。
まず1つ目。
再帰関数で徐々にパスの輪郭が描かれていく版。(strokeの呼び出しが描画する線の数だけの回数必要なので、後述コードより描画の完成に時間がかかる。Google Chromeでは滑らかに、Safariでは滑らかではないけど徐々に描かれていくことを確認できた。Firefoxではstrokeの呼び出しが増えた分(あるいはsetTimeoutが起因?)、描画速度が落ちただけで、後述のコードと描画結果は変わらなかった。ということで、描画結果だけを見たい場合や、Firefoxで実行する場合はこのコードではなく後述の2つ目コードで。Internet Explorerは確認してないから、実行するならとりあえずは後記のコードの方がいいかも。)
C曲線の描画
コード(BBEdit)
var ccurve_canvas = document.getElementById('ccurve_canvas'), $ccurve_canvas = $('#ccurve_canvas'), x0 = 0, y0 = 0, ccurve_ctx = ccurve_canvas.getContext('2d'), ccurve_canvas_width = $('#ccurve_canvas_width').val(), ccurve_canvas_height = $('#ccurve_canvas_height').val(), ccurve_canvas_begin_x = parseFloat($('#ccurve_canvas_begin_x').val()), ccurve_canvas_begin_y = parseFloat($('#ccurve_canvas_begin_y').val()), ccurve_begin_length = parseFloat($('#ccurve_begin_length').val()), ccurve_begin_angle = parseFloat($('#ccurve_begin_angle').val()), ccurve_min_length = parseFloat($('#ccurve_min_length').val()), tm, init = function (width, height, canvas) { canvas.attr({'width': width, 'height': height}); }, setInitialPoint = function (x, y, ctx) { ctx.moveTo(x, y); x0 = x; y0 = y; }, plotLine = function (length, theta, ctx) { x0 += length * Math.cos(theta); y0 += length * Math.sin(theta); ctx.lineTo(x0, y0); ctx.stroke(); }, ccurve = function (length, theta) { var length_tmp, theta_left, theta_right; if (length <= ccurve_min_length) { tm = setTimeout(function() { plotLine(length, theta, ccurve_ctx); }, 0); return; } length_tmp = length * Math.SQRT1_2; theta_left = theta + Math.PI / 4; theta_right = theta - Math.PI / 4; ccurve(length_tmp, theta_left); ccurve(length_tmp, theta_right); }, erase = function () { $ccurve_canvas.attr('height', '0'); }; init(ccurve_canvas_width, ccurve_canvas_height, $ccurve_canvas); ccurve_ctx.translate(0, ccurve_canvas_height); ccurve_ctx.scale(1, -1); setInitialPoint(ccurve_canvas_begin_x, ccurve_canvas_begin_y, ccurve_ctx); ccurve(ccurve_begin_length, ccurve_begin_angle); $('#ccurve_canvas_erase').click(erase);
キャンバス
描画の開始座標
初期値の線
描画下限値:
C曲線のpng形式のイメージを取得 (右クリックでダウンロード可能)
コード(BBEdit)
var $ccurve_div = $('#ccurve_div'), save = function () { var $img = $('<img>'), ccurve_canvas = document.getElementById('ccurve_canvas'); $img.attr({'alt':'ccurve', 'src':ccurve_canvas.toDataURL()}); $ccurve_div.html($img); }, erase = function () { $ccurve_div.html(''); }; save(); $('#ccurve_div_erase').click(erase);
ドラゴン曲線の描画
コード(BBEdit)
var dragon_canvas = document.getElementById('dragon_canvas'), $dragon_canvas = $('#dragon_canvas'), x0 = 0, y0 = 0, dragon_ctx = dragon_canvas.getContext('2d'), dragon_canvas_width = $('#dragon_canvas_width').val(), dragon_canvas_height = $('#dragon_canvas_height').val(), dragon_canvas_begin_x = parseFloat($('#dragon_canvas_begin_x').val()), dragon_canvas_begin_y = parseFloat($('#dragon_canvas_begin_y').val()), dragon_begin_length = parseFloat($('#dragon_begin_length').val()), dragon_begin_angle = parseFloat($('#dragon_begin_angle').val()), dragon_min_length = parseFloat($('#dragon_min_length').val()), init = function (width, height, canvas) { canvas.attr({'width': width, 'height': height}); }, setInitialPoint = function (x, y, ctx) { ctx.moveTo(x, y); x0 = x; y0 = y; }, plotLine = function (length, theta, ctx) { x0 += length * Math.cos(theta); y0 += length * Math.sin(theta); ctx.lineTo(x0, y0); ctx.stroke(); }, dragon = function (length, theta, flip) { var length_tmp, theta_left, theta_right; if (length <= dragon_min_length) { setTimeout(function (){ plotLine(length, theta, dragon_ctx); }, 0); return; } length_tmp = length * Math.SQRT1_2; theta_left = theta + Math.PI / 4; theta_right = theta - Math.PI / 4; if (flip === 1) { dragon(length_tmp, theta_left, 1); dragon(length_tmp, theta_right, -1); } else { dragon(length_tmp, theta_right, 1); dragon(length_tmp, theta_left, -1); } }, erase = function () { $dragon_canvas.attr('height', '0'); }; init(dragon_canvas_width, dragon_canvas_height, $dragon_canvas); dragon_ctx.translate(0, dragon_canvas_height); dragon_ctx.scale(1, -1); setInitialPoint(dragon_canvas_begin_x, dragon_canvas_begin_y, dragon_ctx); dragon(dragon_begin_length, dragon_begin_angle, 1); $('#dragon_canvas_clear').click(erase);
キャンバス
描画の開始座標
初期値の線
描画下限値:
ドラゴン曲線のpng形式のイメージを取得 (右クリックでダウンロード可能)
コード(BBEdit)
var $dragon_div = $('#dragon_div'), save = function () { var $img = $('<img>'), dragon_canvas = document.getElementById('dragon_canvas'); $img.attr({'alt': 'dragon', 'src': dragon_canvas.toDataURL()}); $dragon_div.html($img); }, erase = function () { $dragon_div.html(''); }; save(); $('#dragon_div_erase').click(erase);
2つ目。
最後にまとめてパスの輪郭を描く版(前記のコードの数カ所コメントアウトし、strokeメソッドを最後の方に書き加えて、呼び出しが1回で済むように修正。)
C曲線の描画
コード(BBEdit)
var ccurve1_canvas = document.getElementById('ccurve1_canvas'), $ccurve1_canvas = $('#ccurve1_canvas'), x0 = 0, y0 = 0, ccurve1_ctx = ccurve1_canvas.getContext('2d'), ccurve1_canvas_width = $('#ccurve1_canvas_width').val(), ccurve1_canvas_height = $('#ccurve1_canvas_height').val(), ccurve1_canvas_begin_x = parseFloat($('#ccurve1_canvas_begin_x').val()), ccurve1_canvas_begin_y = parseFloat($('#ccurve1_canvas_begin_y').val()), ccurve1_begin_length = parseFloat($('#ccurve1_begin_length').val()), ccurve1_begin_angle = parseFloat($('#ccurve1_begin_angle').val()), ccurve1_min_length = parseFloat($('#ccurve1_min_length').val()), init = function (width, height, canvas) { canvas.attr({'width': width, 'height': height}); }, setInitialPoint = function (x, y, ctx) { ctx.moveTo(x, y); x0 = x; y0 = y; }, plotLine = function (length, theta, ctx) { x0 += length * Math.cos(theta); y0 += length * Math.sin(theta); ctx.lineTo(x0, y0); // ctx.stroke(); }, ccurve1 = function (length, theta) { var length_tmp, theta_left, theta_right; if (length <= ccurve1_min_length) { // setTimeout(function() { plotLine(length, theta, ccurve1_ctx); // }, 0); return; } length_tmp = length * Math.SQRT1_2; theta_left = theta + Math.PI / 4; theta_right = theta - Math.PI / 4; ccurve1(length_tmp, theta_left); ccurve1(length_tmp, theta_right); }, stop = function () { clearTimeout(tm); }, erase = function () { $ccurve1_canvas.attr('height', '0'); }; init(ccurve1_canvas_width, ccurve1_canvas_height, $ccurve1_canvas); ccurve1_ctx.translate(0, ccurve1_canvas_height); ccurve1_ctx.scale(1, -1); setInitialPoint(ccurve1_canvas_begin_x, ccurve1_canvas_begin_y, ccurve1_ctx); $('#ccurve1_stop').click(stop); ccurve1(ccurve1_begin_length, ccurve1_begin_angle); ccurve1_ctx.stroke(); $('#ccurve1_canvas_erase').click(erase);
キャンバス
描画の開始座標
初期値の線
描画下限値:
C曲線のpng形式のイメージを取得 (右クリックでダウンロード可能)
コード(BBEdit)
var $ccurve1_div = $('#ccurve1_div'), save = function () { var $img = $('<img>'), ccurve1_canvas = document.getElementById('ccurve1_canvas'); $img.attr({'alt':'ccurve1', 'src':ccurve1_canvas.toDataURL()}); $ccurve1_div.html($img); }, erase = function () { $ccurve1_div.html(''); }; save(); $('#ccurve1_div_erase').click(erase);
ドラゴン曲線の描画
コード(BBEdit)
var dragon1_canvas = document.getElementById('dragon1_canvas'), $dragon1_canvas = $('#dragon1_canvas'), x0 = 0, y0 = 0, dragon1_ctx = dragon1_canvas.getContext('2d'), dragon1_canvas_width = $('#dragon1_canvas_width').val(), dragon1_canvas_height = $('#dragon1_canvas_height').val(), dragon1_canvas_begin_x = parseFloat($('#dragon1_canvas_begin_x').val()), dragon1_canvas_begin_y = parseFloat($('#dragon1_canvas_begin_y').val()), dragon1_begin_length = parseFloat($('#dragon1_begin_length').val()), dragon1_begin_angle = parseFloat($('#dragon1_begin_angle').val()), dragon1_min_length = parseFloat($('#dragon1_min_length').val()), init = function (width, height, canvas) { canvas.attr({'width': width, 'height': height}); }, setInitialPoint = function (x, y, ctx) { ctx.moveTo(x, y); x0 = x; y0 = y; }, plotLine = function (length, theta, ctx) { x0 += length * Math.cos(theta); y0 += length * Math.sin(theta); ctx.lineTo(x0, y0); }, dragon1 = function (length, theta, flip) { var length_tmp, theta_left, theta_right; if (length <= dragon1_min_length) { // setTimeout(function (){ plotLine(length, theta, dragon1_ctx); // }, 0); return; } length_tmp = length * Math.SQRT1_2; theta_left = theta + Math.PI / 4; theta_right = theta - Math.PI / 4; if (flip === 1) { dragon1(length_tmp, theta_left, 1); dragon1(length_tmp, theta_right, -1); } else { dragon1(length_tmp, theta_right, 1); dragon1(length_tmp, theta_left, -1); } }, erase = function () { $dragon1_canvas.attr('height', '0'); }; init(dragon1_canvas_width, dragon1_canvas_height, $dragon1_canvas); dragon1_ctx.translate(0, dragon1_canvas_height); dragon1_ctx.scale(1, -1); setInitialPoint(dragon1_canvas_begin_x, dragon1_canvas_begin_y, dragon1_ctx); dragon1(dragon1_begin_length, dragon1_begin_angle, 1); dragon1_ctx.stroke(); $('#dragon1_canvas_clear').click(erase);
キャンバス
描画の開始座標
初期値の線
描画下限値:
ドラゴン曲線のpng形式のイメージを取得 (右クリックでダウンロード可能)
コード(BBEdit)
var $dragon1_div = $('#dragon1_div'), save = function () { var $img = $('<img>'), dragon1_canvas = document.getElementById('dragon1_canvas'); $img.attr({'alt': 'dragon1', 'src': dragon1_canvas.toDataURL()}); $dragon1_div.html($img); }, erase = function () { $dragon1_div.html(''); }; save(); $('#dragon1_div_erase').click(erase);
0 コメント:
コメントを投稿