1.概要
QRコードの符号作成プログラムを実装してみた。同じことを試してみたい人のためにいくつかの情報を残します。
1-1. 特に有用だった参考文献
自分で手を動かしながらQRコードを作ったり 理解するには以下のサイトが極めて有用だった。他のサイトを見る必要がないくらい完結しています:
- 『QRコードをつくってみる』: http://www.swetake.com/qrcode/qr1.html
1-2. 本稿の付加価値
前述のサイトで説明された二つのQRコードを作成しただけなんだけど 上記のサイトで言語化・符号化されていない情報を一部補完します。逆にいうと、元サイトと併せて読まないと何を言っているのかわからないかも:
2. 八文字 "ABCDE123"
前述のサイトの Part1~Part7 では、ABCDE123
という文字列のQRコードを作っています。本章では その補足というか、プログラムを作る上で必要な情報を整理します
2-1. モジュールについて
バージョン1、つまり 21x21 のマスの構造を文字列で表現します。モジュールの基本構造をコードに落とすときに役に立つかもしれない:
// 0:白 1:黒 2:形式用 9:実データ用
111111102999901111111
100000102999901000001
101110102999901011101
101110102999901011101
101110102999901011101
100000102999901000001
111111101010101111111
000000002999900000000
222222122999922222222
999999099999999999999
999999199999999999999
999999099999999999999
999999199999999999999
000000001999999999999
111111102999999999999
100000102999999999999
101110102999999999999
101110102999999999999
101110102999999999999
100000102999999999999
111111102999999999999
2-2. データを埋める順序
上述の 9
の場所にデータを埋めていくのだけれど、その順番がややこしい。右下から幅2の帯が上に行き、ぶつかったら隣にずれて下に行き、ぶつかったら隣にずれて上に行き、、、という順番。上記の21x21の左上が (0,0)
とする座標系では 以下のように巡る:
(20,20),(19,20),(20,19),(19,19),(20,18),(19,18),(20,17),(19,17),(20,16),(19,16),(20,15),(19,15),(20,14),(19,14),(20,13),(19,13),(20,12),(19,12),(20,11),(19,11),(20,10),(19,10),(20,9),(19,9),(18,9),(17,9),(18,10),(17,10),(18,11),(17,11),(18,12),(17,12),(18,13),(17,13),(18,14),(17,14),(18,15),(17,15),(18,16),(17,16),(18,17),(17,17),(18,18),(17,18),(18,19),(17,19),(18,20),(17,20),(16,20),(15,20),(16,19),(15,19),(16,18),(15,18),(16,17),(15,17),(16,16),(15,16),(16,15),(15,15),(16,14),(15,14),(16,13),(15,13),(16,12),(15,12),(16,11),(15,11),(16,10),(15,10),(16,9),(15,9),(14,9),(13,9),(14,10),(13,10),(14,11),(13,11),(14,12),(13,12),(14,13),(13,13),(14,14),(13,14),(14,15),(13,15),(14,16),(13,16),(14,17),(13,17),(14,18),(13,18),(14,19),(13,19),(14,20),(13,20),(12,20),(11,20),(12,19),(11,19),(12,18),(11,18),(12,17),(11,17),(12,16),(11,16),(12,15),(11,15),(12,14),(11,14),(12,13),(11,13),(12,12),(11,12),(12,11),(11,11),(12,10),(11,10),(12,9),(11,9),(12,8),(11,8),(12,7),(11,7),(12,5),(11,5),(12,4),(11,4),(12,3),(11,3),(12,2),(11,2),(12,1),(11,1),(12,0),(11,0),(10,0),(9,0),(10,1),(9,1),(10,2),(9,2),(10,3),(9,3),(10,4),(9,4),(10,5),(9,5),(10,7),(9,7),(10,8),(9,8),(10,9),(9,9),(10,10),(9,10),(10,11),(9,11),(10,12),(9,12),(10,13),(9,13),(10,14),(9,14),(10,15),(9,15),(10,16),(9,16),(10,17),(9,17),(10,18),(9,18),(10,19),(9,19),(10,20),(9,20),(8,12),(7,12),(8,11),(7,11),(8,10),(7,10),(8,9),(7,9),(5,9),(4,9),(5,10),(4,10),(5,11),(4,11),(5,12),(4,12),(3,12),(2,12),(3,11),(2,11),(3,10),(2,10),(3,9),(2,9),(1,9),(0,9),(1,10),(0,10),(1,11),(0,11),(1,12),(0,12)
この順序の作り方は、「ぶつかったら折り返す」というのを愚直に捉えるのではなく、2-1で示した構造を「下端から上端までなめて9のものだけfilterする」というようなロジックで生成した方がよさそうです(というかそうしないと今後のより大きなQRコードの時に登場する位置あわせパターンへの対応ができなくなる?)
また、x=6にあるタイミングパターンのせいで途中で補正が必要になるので注意。モジュール幅が奇数なのに、幅が偶数の帯で尽くせるのもそこに起因。
形式情報は2か所に書き込む。1か所目はバージョン(というかモジュールの大きさ)に依存しないけれど、二つ目は依存します。
(8,0),(8,1),(8,2),(8,3),(8,4),(8,5),(8,7),(8,8),(7,8),(5,8),(4,8),(3,8),(2,8),(1,8),(0,8)
(20,8),(19,8),(18,8),(17,8),(16,8),(15,8),(14,8),(13,8),(8,14),(8,15),(8,16),(8,17),(8,18),(8,19),(8,20)
2-3 完成版の文字列表記
111111100110001111111
100000100000001000001
101110100001001011101
101110100110001011101
101110101100101011101
100000100011101000001
111111101010101111111
000000001101100000000
001100111001111010000
101010000011100101110
111101101011100111010
101011011100111001010
001001110000001011111
000000001101000001011
111111101111000010110
100000100001011100000
101110100100110010011
101110101101000001110
101110101111000011100
100000100000000010100
111111100011111010010
3. 52文字 "abc...xyz"
Part8~Part12は、a..zを二回繰り返した52文字を、バージョン5, レベル Q で符号化をします
3-1. モジュールについて
バージョン5、すなわち 37x37 の構造:
// 0:白 1:黒 2:形式用 9:実データ用
1111111029999999999999999999901111111
1000001029999999999999999999901000001
1011101029999999999999999999901011101
1011101029999999999999999999901011101
1011101029999999999999999999901011101
1000001029999999999999999999901000001
1111111010101010101010101010101111111
0000000029999999999999999999900000000
2222221229999999999999999999922222222
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999999999999
9999990999999999999999999999999999999
9999991999999999999999999999111119999
0000000019999999999999999999100019999
1111111029999999999999999999101019999
1000001029999999999999999999100019999
1011101029999999999999999999111119999
1011101029999999999999999999999999999
1011101029999999999999999999999999999
1000001029999999999999999999999999999
1111111029999999999999999999999999999
手で書いてもよいけれど、以下の順序で上書きすると自動的に(簡単に)作れます
- すべて 9 で初期化
- 形式情報を2で埋める(以下のステップで上書きされるのでさくっと作る)
- x=6 と y=6 のタイミングパターンを書く(これも上書きされるのでモジュールの端から端まで作る)
- 正方形を配置する
- 左下の形式情報の上にある離れ小島1セルを 1で埋める
- 位置あわせパターンを配置する
3-2. データを埋める順序
位置合わせパターンが邪魔をするせいで、変則的な動きになります
(36,36),(35,36),(36,35),(35,35),(36,34),(35,34),(36,33),(35,33),(36,32),(35,32),(36,31),(35,31),(36,30),(35,30),(36,29),(35,29),(36,28),(35,28),(36,27),(35,27),(36,26),(35,26),(36,25),(35,25),(36,24),(35,24),(36,23),(35,23),(36,22),(35,22),(36,21),(35,21),(36,20),(35,20),(36,19),(35,19),(36,18),(35,18),(36,17),(35,17),(36,16),(35,16),(36,15),(35,15),(36,14),(35,14),(36,13),(35,13),(36,12),(35,12),(36,11),(35,11),(36,10),(35,10),(36,9),(35,9),(34,9),(33,9),(34,10),(33,10),(34,11),(33,11),(34,12),(33,12),(34,13),(33,13),(34,14),(33,14),(34,15),(33,15),(34,16),(33,16),(34,17),(33,17),(34,18),(33,18),(34,19),(33,19),(34,20),(33,20),(34,21),(33,21),(34,22),(33,22),(34,23),(33,23),(34,24),(33,24),(34,25),(33,25),(34,26),(33,26),(34,27),(33,27),(34,28),(33,28),(34,29),(33,29),(34,30),(33,30),(34,31),(33,31),(34,32),(33,32),(34,33),(33,33),(34,34),(33,34),(34,35),(33,35),(34,36),(33,36),(32,36),(31,36),(32,35),(31,35),(32,34),(31,34),(32,33),(31,33),(32,27),(31,27),(32,26),(31,26),(32,25),(31,25),(32,24),(31,24),(32,23),(31,23),(32,22),(31,22),(32,21),(31,21),(32,20),(31,20),(32,19),(31,19),(32,18),(31,18),(32,17),(31,17),(32,16),(31,16),(32,15),(31,15),(32,14),(31,14),(32,13),(31,13),(32,12),(31,12),(32,11),(31,11),(32,10),(31,10),(32,9),(31,9),(30,9),(29,9),(30,10),(29,10),(30,11),(29,11),(30,12),(29,12),(30,13),(29,13),(30,14),(29,14),(30,15),(29,15),(30,16),(29,16),(30,17),(29,17),(30,18),(29,18),(30,19),(29,19),(30,20),(29,20),(30,21),(29,21),(30,22),(29,22),(30,23),(29,23),(30,24),(29,24),(30,25),(29,25),(30,26),(29,26),(30,27),(29,27),(30,33),(29,33),(30,34),(29,34),(30,35),(29,35),(30,36),(29,36),(28,36),(27,36),(28,35),(27,35),(28,34),(27,34),(28,33),(27,33),(27,32),(27,31),(27,30),(27,29),(27,28),(28,27),(27,27),(28,26),(27,26),(28,25),(27,25),(28,24),(27,24),(28,23),(27,23),(28,22),(27,22),(28,21),(27,21),(28,20),(27,20),(28,19),(27,19),(28,18),(27,18),(28,17),(27,17),(28,16),(27,16),(28,15),(27,15),(28,14),(27,14),(28,13),(27,13),(28,12),(27,12),(28,11),(27,11),(28,10),(27,10),(28,9),(27,9),(28,8),(27,8),(28,7),(27,7),(28,5),(27,5),(28,4),(27,4),(28,3),(27,3),(28,2),(27,2),(28,1),(27,1),(28,0),(27,0),(26,0),(25,0),(26,1),(25,1),(26,2),(25,2),(26,3),(25,3),(26,4),(25,4),(26,5),(25,5),(26,7),(25,7),(26,8),(25,8),(26,9),(25,9),(26,10),(25,10),(26,11),(25,11),(26,12),(25,12),(26,13),(25,13),(26,14),(25,14),(26,15),(25,15),(26,16),(25,16),(26,17),(25,17),(26,18),(25,18),(26,19),(25,19),(26,20),(25,20),(26,21),(25,21),(26,22),(25,22),(26,23),(25,23),(26,24),(25,24),(26,25),(25,25),(26,26),(25,26),(26,27),(25,27),(26,28),(25,28),(26,29),(25,29),(26,30),(25,30),(26,31),(25,31),(26,32),(25,32),(26,33),(25,33),(26,34),(25,34),(26,35),(25,35),(26,36),(25,36),(24,36),(23,36),(24,35),(23,35),(24,34),(23,34),(24,33),(23,33),(24,32),(23,32),(24,31),(23,31),(24,30),(23,30),(24,29),(23,29),(24,28),(23,28),(24,27),(23,27),(24,26),(23,26),(24,25),(23,25),(24,24),(23,24),(24,23),(23,23),(24,22),(23,22),(24,21),(23,21),(24,20),(23,20),(24,19),(23,19),(24,18),(23,18),(24,17),(23,17),(24,16),(23,16),(24,15),(23,15),(24,14),(23,14),(24,13),(23,13),(24,12),(23,12),(24,11),(23,11),(24,10),(23,10),(24,9),(23,9),(24,8),(23,8),(24,7),(23,7),(24,5),(23,5),(24,4),(23,4),(24,3),(23,3),(24,2),(23,2),(24,1),(23,1),(24,0),(23,0),(22,0),(21,0),(22,1),(21,1),(22,2),(21,2),(22,3),(21,3),(22,4),(21,4),(22,5),(21,5),(22,7),(21,7),(22,8),(21,8),(22,9),(21,9),(22,10),(21,10),(22,11),(21,11),(22,12),(21,12),(22,13),(21,13),(22,14),(21,14),(22,15),(21,15),(22,16),(21,16),(22,17),(21,17),(22,18),(21,18),(22,19),(21,19),(22,20),(21,20),(22,21),(21,21),(22,22),(21,22),(22,23),(21,23),(22,24),(21,24),(22,25),(21,25),(22,26),(21,26),(22,27),(21,27),(22,28),(21,28),(22,29),(21,29),(22,30),(21,30),(22,31),(21,31),(22,32),(21,32),(22,33),(21,33),(22,34),(21,34),(22,35),(21,35),(22,36),(21,36),(20,36),(19,36),(20,35),(19,35),(20,34),(19,34),(20,33),(19,33),(20,32),(19,32),(20,31),(19,31),(20,30),(19,30),(20,29),(19,29),(20,28),(19,28),(20,27),(19,27),(20,26),(19,26),(20,25),(19,25),(20,24),(19,24),(20,23),(19,23),(20,22),(19,22),(20,21),(19,21),(20,20),(19,20),(20,19),(19,19),(20,18),(19,18),(20,17),(19,17),(20,16),(19,16),(20,15),(19,15),(20,14),(19,14),(20,13),(19,13),(20,12),(19,12),(20,11),(19,11),(20,10),(19,10),(20,9),(19,9),(20,8),(19,8),(20,7),(19,7),(20,5),(19,5),(20,4),(19,4),(20,3),(19,3),(20,2),(19,2),(20,1),(19,1),(20,0),(19,0),(18,0),(17,0),(18,1),(17,1),(18,2),(17,2),(18,3),(17,3),(18,4),(17,4),(18,5),(17,5),(18,7),(17,7),(18,8),(17,8),(18,9),(17,9),(18,10),(17,10),(18,11),(17,11),(18,12),(17,12),(18,13),(17,13),(18,14),(17,14),(18,15),(17,15),(18,16),(17,16),(18,17),(17,17),(18,18),(17,18),(18,19),(17,19),(18,20),(17,20),(18,21),(17,21),(18,22),(17,22),(18,23),(17,23),(18,24),(17,24),(18,25),(17,25),(18,26),(17,26),(18,27),(17,27),(18,28),(17,28),(18,29),(17,29),(18,30),(17,30),(18,31),(17,31),(18,32),(17,32),(18,33),(17,33),(18,34),(17,34),(18,35),(17,35),(18,36),(17,36),(16,36),(15,36),(16,35),(15,35),(16,34),(15,34),(16,33),(15,33),(16,32),(15,32),(16,31),(15,31),(16,30),(15,30),(16,29),(15,29),(16,28),(15,28),(16,27),(15,27),(16,26),(15,26),(16,25),(15,25),(16,24),(15,24),(16,23),(15,23),(16,22),(15,22),(16,21),(15,21),(16,20),(15,20),(16,19),(15,19),(16,18),(15,18),(16,17),(15,17),(16,16),(15,16),(16,15),(15,15),(16,14),(15,14),(16,13),(15,13),(16,12),(15,12),(16,11),(15,11),(16,10),(15,10),(16,9),(15,9),(16,8),(15,8),(16,7),(15,7),(16,5),(15,5),(16,4),(15,4),(16,3),(15,3),(16,2),(15,2),(16,1),(15,1),(16,0),(15,0),(14,0),(13,0),(14,1),(13,1),(14,2),(13,2),(14,3),(13,3),(14,4),(13,4),(14,5),(13,5),(14,7),(13,7),(14,8),(13,8),(14,9),(13,9),(14,10),(13,10),(14,11),(13,11),(14,12),(13,12),(14,13),(13,13),(14,14),(13,14),(14,15),(13,15),(14,16),(13,16),(14,17),(13,17),(14,18),(13,18),(14,19),(13,19),(14,20),(13,20),(14,21),(13,21),(14,22),(13,22),(14,23),(13,23),(14,24),(13,24),(14,25),(13,25),(14,26),(13,26),(14,27),(13,27),(14,28),(13,28),(14,29),(13,29),(14,30),(13,30),(14,31),(13,31),(14,32),(13,32),(14,33),(13,33),(14,34),(13,34),(14,35),(13,35),(14,36),(13,36),(12,36),(11,36),(12,35),(11,35),(12,34),(11,34),(12,33),(11,33),(12,32),(11,32),(12,31),(11,31),(12,30),(11,30),(12,29),(11,29),(12,28),(11,28),(12,27),(11,27),(12,26),(11,26),(12,25),(11,25),(12,24),(11,24),(12,23),(11,23),(12,22),(11,22),(12,21),(11,21),(12,20),(11,20),(12,19),(11,19),(12,18),(11,18),(12,17),(11,17),(12,16),(11,16),(12,15),(11,15),(12,14),(11,14),(12,13),(11,13),(12,12),(11,12),(12,11),(11,11),(12,10),(11,10),(12,9),(11,9),(12,8),(11,8),(12,7),(11,7),(12,5),(11,5),(12,4),(11,4),(12,3),(11,3),(12,2),(11,2),(12,1),(11,1),(12,0),(11,0),(10,0),(9,0),(10,1),(9,1),(10,2),(9,2),(10,3),(9,3),(10,4),(9,4),(10,5),(9,5),(10,7),(9,7),(10,8),(9,8),(10,9),(9,9),(10,10),(9,10),(10,11),(9,11),(10,12),(9,12),(10,13),(9,13),(10,14),(9,14),(10,15),(9,15),(10,16),(9,16),(10,17),(9,17),(10,18),(9,18),(10,19),(9,19),(10,20),(9,20),(10,21),(9,21),(10,22),(9,22),(10,23),(9,23),(10,24),(9,24),(10,25),(9,25),(10,26),(9,26),(10,27),(9,27),(10,28),(9,28),(10,29),(9,29),(10,30),(9,30),(10,31),(9,31),(10,32),(9,32),(10,33),(9,33),(10,34),(9,34),(10,35),(9,35),(10,36),(9,36),(8,28),(7,28),(8,27),(7,27),(8,26),(7,26),(8,25),(7,25),(8,24),(7,24),(8,23),(7,23),(8,22),(7,22),(8,21),(7,21),(8,20),(7,20),(8,19),(7,19),(8,18),(7,18),(8,17),(7,17),(8,16),(7,16),(8,15),(7,15),(8,14),(7,14),(8,13),(7,13),(8,12),(7,12),(8,11),(7,11),(8,10),(7,10),(8,9),(7,9),(5,9),(4,9),(5,10),(4,10),(5,11),(4,11),(5,12),(4,12),(5,13),(4,13),(5,14),(4,14),(5,15),(4,15),(5,16),(4,16),(5,17),(4,17),(5,18),(4,18),(5,19),(4,19),(5,20),(4,20),(5,21),(4,21),(5,22),(4,22),(5,23),(4,23),(5,24),(4,24),(5,25),(4,25),(5,26),(4,26),(5,27),(4,27),(5,28),(4,28),(3,28),(2,28),(3,27),(2,27),(3,26),(2,26),(3,25),(2,25),(3,24),(2,24),(3,23),(2,23),(3,22),(2,22),(3,21),(2,21),(3,20),(2,20),(3,19),(2,19),(3,18),(2,18),(3,17),(2,17),(3,16),(2,16),(3,15),(2,15),(3,14),(2,14),(3,13),(2,13),(3,12),(2,12),(3,11),(2,11),(3,10),(2,10),(3,9),(2,9),(1,9),(0,9),(1,10),(0,10),(1,11),(0,11),(1,12),(0,12),(1,13),(0,13),(1,14),(0,14),(1,15),(0,15),(1,16),(0,16),(1,17),(0,17),(1,18),(0,18),(1,19),(0,19),(1,20),(0,20),(1,21),(0,21),(1,22),(0,22),(1,23),(0,23),(1,24),(0,24),(1,25),(0,25),(1,26),(0,26),(1,27),(0,27),(1,28),(0,28)
(8,0),(8,1),(8,2),(8,3),(8,4),(8,5),(8,7),(8,8),(7,8),(5,8),(4,8),(3,8),(2,8),(1,8),(0,8)
(36,8),(35,8),(34,8),(33,8),(32,8),(31,8),(30,8),(29,8),(8,30),(8,31),(8,32),(8,33),(8,34),(8,35),(8,36)
3.3 完成形
1111111000101100011100000110101111111
1000001010011000010110100110001000001
1011101011011101011110111111001011101
1011101000000001011000110111001011101
1011101000110101101010001111101011101
1000001001011100010010101010001000001
1111111010101010101010101010101111111
0000000000100100110110101111100000000
0111011000000101101101010110100000110
0110110010101100010101010101010100000
1000111100111100110000010010000100000
1010110000011001100100110101100001100
0000101000001001100100111110011010111
0111100001111100101010101011111110001
1110101001110100000110101010100011110
1100110001001010011011111010010100010
0010111110110110101010001101100100101
1000000010111110100111000011011000101
1001011000100101100101100011100011011
1101000110100001110100110010110011010
0001011100001000000011000010110111010
0011100101111001111011010000100100010
0101011000000001000001010010011101000
0000000011010101101010111111000101111
0100101100110111010110110001111111101
1001110101001000101110001101101111011
1001101010100111100010101100010111110
0100010100100000101001101011011100010
1111111101101010011101100101111111110
0000000011100010000011011000100010101
1111111000100101001100101000101011111
1000001010011100000111000011100011011
1011101000111011101010010010111110011
1011101011111000110001000110011011010
1011101010011000100001000000111111010
1000001011010111101100111111010111100
1111111000101101011010111110001100111
なお、バージョン5だと、ECC含めたデータコード数 134byte = 1072bit に対し、セルの数は 1079 個ある。つまり最後の 7セルはどう使うかが決まっていない。具体的には以下の座標
(0,25),(1,26),(0,26),(1,27),(0,27),(1,28),(0,28)
元サイトではこの7つのフラグを立てていた(そのあとにXORマスク処理を実施)が、この7つのフラグを折って(そのあとにXORマスク処理を実施)も、QRコードリーダは問題なく動作した。
4. 符号の可視化
ブラウザに表示するHTMLファイル。コード内の S
を変えると遊べます。
<html><body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="qrarea"></div>
<script>
const S = `
1111111000101100011100000110101111111
1000001010011000010110100110001000001
1011101011011101011110111111001011101
1011101000000001011000110111001011101
1011101000110101101010001111101011101
1000001001011100010010101010001000001
1111111010101010101010101010101111111
0000000000100100110110101111100000000
0111011000000101101101010110100000110
0110110010101100010101010101010100000
1000111100111100110000010010000100000
1010110000011001100100110101100001100
0000101000001001100100111110011010111
0111100001111100101010101011111110001
1110101001110100000110101010100011110
1100110001001010011011111010010100010
0010111110110110101010001101100100101
1000000010111110100111000011011000101
1001011000100101100101100011100011011
1101000110100001110100110010110011010
0001011100001000000011000010110111010
0011100101111001111011010000100100010
0101011000000001000001010010011101000
0000000011010101101010111111000101111
0100101100110111010110110001111111101
1001110101001000101110001101101111011
1001101010100111100010101100010111110
0100010100100000101001101011011100010
1111111101101010011101100101111111110
0000000011100010000011011000100010101
1111111000100101001100101000101011111
1000001010011100000111000011100011011
1011101000111011101010010010111110011
1011101011111000110001000110011011010
1011101010011000100001000000111111010
1000001011010111101100111111010111100
1111111000101101011010111110001100111
`.replace(/\s/g, '')
const L = Math.floor(Math.sqrt(S.length));
const putBox = (x, y, size, svg) => svg.append('rect').attr('x', x * size).attr('y', y * size).attr('width', size).attr('height', size).style('fill', '#black');
window.onload = async () => {
const svg = d3.select('#qrarea').append('svg').attr('width', 400).attr('height', 400);
[...S].forEach((c, idx) => { if (c === '1') { putBox(idx % L, Math.floor(idx/L), 5, svg); } });
};
</script></body></html>