概要
意外と検索してもヒットしなかったので、記事にまとめます。
大まかに言うと、等高線のplotをします。
やりたいこと
今回は $y^2 = x^3 - 4 x$ が描きたいグラフです。
$f(x,y) = x^3 - 4 x - y^2$ に対して、 $|f(x,y)| \le \epsilon$ を満たす点を描画します。
結果の例
結果はこんな感じです。
epsilon=0.1
ゼロの近傍を $-0.1 \le f(x,y) \le 0.1$ とした場合のグラフです。
epsilon=1.0
やっていることが分かりにくいので、ゼロの近傍を $-10 \le f(x,y) \le 10$ までに広げたものです。
方針
陰関数が描きたいというのに、等高線で我慢すれば良いんじゃね?と思ったのですが、見たいもの以外の情報が入りすぎるので情報を削ります。
- 楽するためには絶対値を取ります
- $f(x,y)$ は、ゼロをまたぐところで符号が変わります。
- 符号が変わると、
contour
で描く等高線は別の色になります。 - 正負で同じ色をつける colorscale はなさそうです。それに、colorscaleをカスタマイズするのは面倒です。
- colorscale の
z_max
とepsilon
を合わせます- $x^3$ のオーダーですし、スケールが広ければ広いほど 0 周辺が目立たなくなります。そうなると本末転倒ですから、カラーバーの目盛りを 0 ~
epsilon
までの範囲に制限します。
- $x^3$ のオーダーですし、スケールが広ければ広いほど 0 周辺が目立たなくなります。そうなると本末転倒ですから、カラーバーの目盛りを 0 ~
JavaScriptのコード例
function window_function(z, epsilon) {
positive_z = Math.abs(z);
if (positive_z > epsilon) return epsilon;
return positive_z;
}
let cd_ary_x = [];
let cd_ary_y = [];
let cd_ary_z = [];
let x_min = -5;
let x_max = 5;
let x_sample_size = 600;
let dx = (x_max - x_min) / x_sample_size;
let y_min = -5;
let y_max = 5;
let y_sample_size = 600;
let dy = (y_max - y_min) / y_sample_size;
let epsilon = 0.1;
for (let x = x_min; x <= x_max; x += dx) {
for (let y = y_min; y <= y_max; y += dy) {
cd_ary_x.push(x);
cd_ary_y.push(y);
cd_ary_z.push(window_function(x*x*x - 4 * x - y * y, epsilon));
}
}
var cd_trace = {
color:'rgb(0,0,0)',
x: cd_ary_x,
y: cd_ary_y,
z: cd_ary_z,
type: 'contour',
colorscale: 'Hot',
z_min: 0,
z_max: epsilon
};
var cd_layout = {
hovermode:'closest',
autosize: false, width: 600, height: 600,
yaxis: { l: 10, r: 10, b: 10, t: 10, pad: 4 },
paper_bgcolor: '#efefef'
};
Plotly.newPlot('jacobi_elliptic_functions_plot_area', [cd_trace], cd_layout, {displayModeBar: false});
呼び出し元のコード
今回は AsciiDoc に埋め込んだのでこんな感じです。
ifndef::leveloffset[]
:toc: left
:toclevels: 3
ifeval::["{backend}" == "html5"]
pass:[<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>]
endif::[]
endif::[]
include::header.adoc[]
== Jacobi elliptic functions
ifeval::["{backend}" == "html5"]
**楕円関数: f(_x_) = _x_^3^ -4__x__ - _y_^2^**
[pass]
++++
<div id="jacobi_elliptic_functions_plot_area"><!-- Plotly chart will be drawn inside this DIV --></div>
<script>
include::js/jacobi_elliptic_functions.js[]
</script>
++++
endif::[]
htmlで試したい人のために、htmlに仕立て上げたものも参考に載せます。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Pragma" content="no-cache">
<meta charset="utf-8">
<script type="text/javascript" src="https://cdn.plot.ly/plotly-latest.min.js" charset="utf-8"></script>
<title>Jacobi elliptic functions</title>
</head>
<body>
<div id="jacobi_elliptic_functions_plot_area"></div>
<script language="JavaScript" type="text/javascript">
function window_function(z, epsilon) {
positive_z = Math.abs(z);
if (positive_z > epsilon) return epsilon;
return positive_z;
}
let cd_ary_x = [];
let cd_ary_y = [];
let cd_ary_z = [];
let x_min = -5;
let x_max = 5;
let x_sample_size = 600;
let dx = (x_max - x_min) / x_sample_size;
let y_min = -5;
let y_max = 5;
let y_sample_size = 600;
let dy = (y_max - y_min) / y_sample_size;
let epsilon = 0.2;
for (let x = x_min; x <= x_max; x += dx) {
for (let y = y_min; y <= y_max; y += dy) {
cd_ary_x.push(x);
cd_ary_y.push(y);
cd_ary_z.push(window_function(x*x*x - 4 * x - y * y, epsilon));
}
}
var cd_trace = {
color:'rgb(0,0,0)',
x: cd_ary_x,
y: cd_ary_y,
z: cd_ary_z,
type: 'contour',
colorscale: 'Hot',
z_min: 0,
z_max: epsilon
};
var cd_layout = {
hovermode:'closest',
autosize: false, width: 600, height: 600,
yaxis: { l: 10, r: 10, b: 10, t: 10, pad: 4 },
paper_bgcolor: '#efefef'
};
Plotly.newPlot('jacobi_elliptic_functions_plot_area', [cd_trace], cd_layout, {displayModeBar: false});
</script>
</body>
</html>