plotly.js で陰関数を描画する

## 概要
意外と検索してもヒットしなかったので、記事にまとめます。
大まかに言うと、等高線の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$ とした場合のグラフです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/bb146eed-8171-b1ee-a6eb-9899180e12e3.png)

**epsilon=1.0**
やっていることが分かりにくいので、ゼロの近傍を $-10 \le f(x,y) \le 10$ までに広げたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326797/d70241ff-5f9c-5364-2b9e-e4fc5af0d124.png)

## 方針
陰関数が描きたいというのに、等高線で我慢すれば良いんじゃね？と思ったのですが、見たいもの以外の情報が入りすぎるので情報を削ります。

* 楽するためには絶対値を取ります
  * $f(x,y)$ は、ゼロをまたぐところで符号が変わります。
  * 符号が変わると、 `contour` で描く等高線は別の色になります。
  * 正負で同じ色をつける colorscale はなさそうです。それに、colorscaleをカスタマイズするのは面倒です。
* colorscale の `z_max` と `epsilon` を合わせます
  * $x^3$ のオーダーですし、スケールが広ければ広いほど 0 周辺が目立たなくなります。そうなると本末転倒ですから、カラーバーの目盛りを 0 ～ `epsilon` までの範囲に制限します。

## JavaScriptのコード例

```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 に埋め込んだのでこんな感じです。

```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に仕立て上げたものも参考に載せます。

```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>
```

## 参考

* [Colorscales |  JavaScript  | Plotly  ](https://plotly.com/javascript/colorscales/)
* [楕円曲線の有理点 | 数理女子](http://www.suri-joshi.jp/enjoy/rational_points_of_elliptic_curve/)