素のHTML+JavaScriptで複素関数を描画する
はじめに
特別なライブラリを使わなくても、HTMLのcanvas要素を使ってJavaScriptで色々描ける。
例えば複素対数関数とかをこんな感じで。
canvas要素の取り扱いについてはこちらから詳しくCanvasRenderingContext2D - Web API | MDN
とりあえずなんか描く
body要素にcanvasを設置してグラデーションを描いてみる
👇コード全文
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>famous canvas</title> <style>canvas {border: 1px solid black;}</style> <script> window.onload = () => { // 👇JavaScriptでcanvas要素を取得、キャンバスサイズの定義とコンテキストの取得 const canvas = document.getElementById('famousCanvas'); const size = 256; [canvas.width, canvas.height] = [size, size]; const context = canvas.getContext('2d'); // 👇グラデーションを描画。fillStyleで色指定、fillRectで描画座標指定。 for(let i = 0; i < size; i++){ for(let j = 0; j < size; j++){ context.fillStyle = `rgba(${i}, ${j}, 128, 1)` context.fillRect(i, j, 1, 1); } } }; </script> </head> // 👇canvas要素を置く。 <body><canvas id="famousCanvas"/></body> </html>
👇結果
偏角を色に変換する
直交座標系x, yの偏角を色環に変換する。
原点(0,0)を視点としてある点(x, y)を通る半直線が下に示す六角形のどの辺に交わるかでRGBの値を決定する。
👇コード(script部分のみ、rectToRgba関数が該当部分。)
const rectToRgba = (x, y) => { const max = 255; const sqrt3 = Math.sqrt(3); // 0点は例外処理。 if (x == 0 && y > 0) return [max*0.5, max, 0]; if (x == 0 && y < 0) return [max*0.5, 0, max]; if (x > 0 && y == 0) return [max, 0, 0]; if (x < 0 && y == 0) return [0, max, max]; const tan = y / x; const cot = x / y; // 場合わけしてrgb算定(上の六角形と見比べるとわかりやすいかも) if (0 < tan && tan < sqrt3) return y > 0 ? [max, tan/sqrt3*max, 0] : [0, (1-tan/sqrt3)*max, max] else if (-1/sqrt3 < cot && cot < 1/sqrt3) return y > 0 ? [(1+cot*sqrt3)*max*0.5, max, 0] : [(1-cot*sqrt3)*max*0.5, 0, max] else return y > 0 ? [0, max, (1+tan)*max] : [max, 0, -tan*max] }; window.onload = () => { const canvas = document.getElementById('famousCanvas'); const size = 256; [canvas.width, canvas.height] = [size, size]; const context = canvas.getContext('2d'); for(let i = -size*0.5; i < size*0.5; i++){ for(let j = -size*0.5; j < size*0.5; j++){ const rgbArray = rectToRgba(i, j); context.fillStyle = `rgba(${rgbArray[0]}, ${rgbArray[1]}, ${rgbArray[2]}, 1)` context.fillRect(i+size*0.5, -j+size*0.5-1, 1, 1); } } };
👇結果(恒等写像f(z) = zの図示になっている)
いろんな複素関数を描いてみる
先ほどのコードにもう少してを加えて、いろいろ複素関数を描いてみる
👇コード全文 (complexFunction()で複素関数の定義するようにした。)
const complexFunction = (x, y) => { // ここに複素関数の定義を書く。たとえばf(z)=z^2なら以下の通り。 return [x*x - y*y, 2*x*y]; }; const rectToRgba = (x, y) => { const max = 255; const sqrt3 = Math.sqrt(3); if (x == 0 && y > 0) return [max*0.5, max, 0]; if (x == 0 && y < 0) return [max*0.5, 0, max]; if (x > 0 && y == 0) return [max, 0, 0]; if (x < 0 && y == 0) return [0, max, max]; const tan = y / x; const cot = x / y; if (0 < tan && tan < sqrt3) return y > 0 ? [max, tan/sqrt3*max, 0] : [0, (1-tan/sqrt3)*max, max] else if (-1/sqrt3 < cot && cot < 1/sqrt3) return y > 0 ? [(1+cot*sqrt3)*max*0.5, max, 0] : [(1-cot*sqrt3)*max*0.5, 0, max] else return y > 0 ? [0, max, (1+tan)*max] : [max, 0, -tan*max] }; window.onload = () => { const canvas = document.getElementById('famousCanvas'); const size = 256; [canvas.width, canvas.height] = [size, size]; const context = canvas.getContext('2d'); for(let i = -size*0.5; i < size*0.5; i++){ for(let j = -size*0.5; j < size*0.5; j++){ const [x, y] = [i/size*5, j/size*5]; const rgbArray = rectToRgba(...complexFunction(x, y)); context.fillStyle = `rgba(${rgbArray[0]}, ${rgbArray[1]}, ${rgbArray[2]}, 1)` context.fillRect(i+size*0.5, -j+size*0.5-1, 1, 1); } } };
👇結果
f(z) = z*z = (x*x - y*y, 2*x*y)
色環が2周しているのがわかる🤔
f(z) = log(z) = (log(sqrt(x*x + y*y)), atan2(y, x))
二次元ではリーマン面が表示しきれない🤔
f(z) = exp(z) = (exp(x)*cos(y), exp(x)*sin(y))
偏角はyだけに依存していることがわかる🤔
f(z) = sin(z) = (sin(x)*cosh(y), cos(x)*sinh(y));
🤔
以上