信号に関する授業を聴いていたらふと思い立ったのでやってみた。

コード

index.html

個人的テンプレを書く。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>0-1 Signal</title>
  </head>
  <body>
    <h1>0-1 Signale</h1>
    <svg>
    </svg>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>

script.js

JavaScriptでflatMap使うのはこれが初めてかも。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const format = (data, w) => {
  const pairs = d3.pairs(data);
  const deltas = pairs.flatMap(e => {
    let sig = e.toString()
    if (sig == '0,0') {
      return [[1,0]];
    } else if (sig == '0,1') {
      return [[1,0],[0,-1]];
    } else if (sig == '1,0') {
      return [[1,0],[0,1]];
    } else if (sig == '1,1') {
      return [[1,0]];
    } else {
      throw new Error('invalid element.');
    }
  });
  const points = deltas.reduce((acc, e) => {
    const back = acc[acc.length - 1].slice();
    back[0] += w * e[0];
    back[1] += w * e[1];
    return acc.concat([back])
  }, [[0,0]]);
  return points;
};

const [svgWidth, svgHeight] = [800, 800];
const svg = d3.select('svg')
  .attr('width', svgWidth)
  .attr('height', svgHeight);

const pad = 70;
const render = (data) => {
  svg.selectAll('*').remove();
  svg.append('path')
    .datum(data)
    .attr('transform', `translate(${pad}, ${pad})`)
    .attr('stroke', 'black')
    .attr('fill', 'none')
    .attr('d', d3.line()
        .x(d => d[0])
        .y(d => d[1]));
};

render(format([0,0,1,0,1,0,1,1,1,1,0,0], 50));

実行結果

説明

format

01の情報を、path用の頂点データに変換する関数。

隣り合う数字によって上に上がるか下に下がるかが変わってくるため、まず隣り合う頂点をまとめたデータをd3.pairsで作成する。次にそれを元に進み具合を表した配列deltaを作成する。例えば0,1の場合、「右に1進んで上に1上がる」という動きをするため、配列[[1,0],[0,-1]]を返すようにしている。作った増分データから、実際の頂点データを作成したものがpointsとなる。ここで、1進んだり上がったりだと、svg上ではあまりにも小さすぎるので、引数wを掛けて拡大している。

render

formatが完成すれば話は早い。ここでは実際の描画を担う。単にpath要素にデータを結びつけて、d3.lineを使って折れ線を生成しているだけ。