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 コメント:
コメントを投稿