JavaScript
点字

Unicodeの点字でお絵かき

5000000000000000yen.js
str = `

⠀⠀⠀⠀⠀⠀⠀⣤⣤⣤⣤⣤⣤⡄⠀⠀⣀⣤⣤⣤⣤⡄⠀⠀⠀⣀⣤⣤⣤⣤⡀⠀⠀⠀⣀⣤⣤⣤⣤⡀⠀⠀⣀⡀⠀⣠⣶⠆⣴⡖⠀⣀⡀⠀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⡄
⠀⠀⠀⠀⠀⣀⣼⡿⠉⠉⠉⠉⠉⠀⣠⣾⡿⠋⠉⠙⣿⡿⠀⣠⣾⠟⠋⠉⢹⣿⡗⠀⣠⣾⠟⠋⠉⢹⣿⡇⠀⠀⣿⣷⣠⣿⠃⣼⣟⣤⡾⠟⠁⣀⣾⡟⠉⠉⣹⡿⠉⠉⢉⣿⠏⠀
⠀⠀⠀⠀⣠⣾⣿⣶⣶⣶⣦⠀⠀⣴⣿⠋⠀⠀⠀⣸⣿⢇⣼⡿⠃⠀⠀⠀⣼⣿⢃⣾⡿⠁⠀⠀⠀⣾⣿⠁⠀⠀⠛⣩⣿⢃⣾⣟⣝⠋⠀⠀⣀⣾⣟⣀⣀⣴⣿⣁⣀⣠⣿⠏⠀⠀
⠀⠀⠀⠀⠛⠋⠁⠀⠀⣿⡿⠀⣼⣿⠃⠀⠀⣀⣼⣿⠋⣾⣿⠁⠀⠀⣀⣼⡿⢃⣾⡿⠁⠀⠀⣀⣾⡿⠁⣴⣶⠾⣿⡿⢃⣾⡟⠙⣿⣷⠄⣀⣾⠟⠛⠛⠛⠛⠛⠛⢻⣿⠏⠀⠀⠀
⠀⠀⣰⣶⣆⣀⣀⣤⣾⠟⠁⣰⣿⣯⣀⣀⣤⣾⠟⠁⣸⣿⣧⣀⣀⣴⣿⠟⠁⣸⣿⣇⣀⣀⣴⣿⠟⠀⡈⣁⣤⣾⠟⣀⣾⠏⠀⣀⣾⠄⣠⣾⠏⠀⠀⠀⠀⠀⠀⣠⣿⠏⠀⠀⠀⠀
⠀⠀⡈⠛⠿⠿⠛⠋⠁⠀⠀⠀⠛⠿⠿⠛⠋⠁⠀⠀⡈⠛⠿⠟⠛⠋⠀⠀⠀⡈⠛⠿⠟⠛⠉⠀⠀⣶⡿⠟⠋⠀⠀⠾⢿⣷⠾⠿⠋⣠⣿⠏⠀⠀⠀⠀⡸⠿⠶⠿⠋⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣤⢤⣄⠀⣠⣶⠦⠀⠀⠀⠀⠀⡰⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡴⢟⣯⣄⣸⣿⣷⠿⠥⣴⣾⠄⠀⠀⠀⣴⡿⠋⠀⠀⠀⠀⠀⠀⠀⣀⣿⡆⠀⠀⠀⠀⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢉⣴⠿⠛⣷⡴⢋⣿⠟⠞⠋⠀⠀⠀⣀⣼⠟⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠟⠀⡀⠀⠀⠀⣈⣿⡀⠀⠀⠀⠀⠀⠀⣰⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⢶⣿⠓⣶⣾⠛⣡⣿⡟⠀⠀⠀⠀⠀⣀⣾⠋⠀⠀⠀⠀⠀⡀⠀⠀⠀⣾⠏⣠⠞⠀⠀⣀⣠⣼⡿⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠃⣰⡿⣁⣾⠟⣰⣷⡀⠀⠀⠀⠀⣾⡇⠀⣀⣀⣤⡶⠋⠀⠀⠀⡸⣿⣿⠁⠀⠀⠀⠀⠙⠉⠀⠀⠀⠀⠀⣀⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠿⠋⢉⡿⠟⠋⠀⠀⣸⠟⠋⠀⠀⠀⠀⠻⠿⠿⠛⠋⠁⠀⠀⠀⠀⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡘⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
`
console.log(str)

点文字ジェネレータ (jsdo.it)

JavaScript(ES2015)で点字を扱う方法についてのメモ。
点字は見た目からして2進数と親和性が高い。
ビット演算を学ぶのにいいかも?

Canvasについては説明なし。
点字の読み方も説明なし。

点字の種類

「6点点字(2×3)」と「8点点字(2×4)」の2種類がある。

6点点字

UnicodeのU+2800U+283Fの64文字。
サイコロの6の目のような2×3の点が並ぶ。
点字の各点にはそれぞれ左上から順番に番号が振られている。

①④
②⑤
③⑥

Unicodeの点字はその番号をn桁目とする2進数の順番に並んでいる。
①③⑤に点を打つなら010101で21番目。
Unicodeの文字の名称もBRAILLE PATTERN DOTS-135等そのまま。

①③⑤と②④⑥に点を打つ点字を取得
var dots135 = 0b010101 + 0x2800; //0bXXは2進数リテラル
String.fromCodePoint(dots135);
//=> ⠕

var dots246 = 0b101010 + 0x2800;
String.fromCodePoint(dots246);
//=> ⠪

全て取得するとこう。

var all = "";
for(var i = 0; i <= 0x3F; i++)
  all += String.fromCodePoint(i + 0x2800);
⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿

8点点字

UnicodeのU+2800U+28FFの256文字。

世界で使われている点字はほぼ全て6点点字。
8点点字は漢字等にしか使われない。Wikipedia: 8-dot braille scripts
この記事ではもちろん8点点字を使う。

点の番号は6点点字の下に点⑦⑧が後付けされる形。

①④
②⑤
③⑥
⑦⑧

このままでは2進数そのままの順番で扱えず都合が悪い。⑦の位置に④が来ないと困る。
2進数そのままの順番で点字を得られる関数を作っておく。

2進数から点字へ
function numToBrailleLetter(n){
  var flags = 0;
  flags += (n & 0b00001000) << 3; //⑦を左シフト
  flags += (n & 0b01110000) >> 1; //④⑤⑥を右シフト
  flags += (n & 0b10000111);      //①②③⑧はそのまま
  return String.fromCodePoint(flags + 0x2800);
}

// 使用例:
var n = 0b11110000;//右1列の点
console.log(numToBrailleLetter(n));
//=> ⢸

ついでに逆も作る。使う予定は無し。

点字から2進数へ
function brailleLetterToNum(c){
  var u = c.codePointAt(0);

  var flags = 0;
  flags += (u & 0b01000000) >> 3;
  flags += (u & 0b00111000) << 1;
  flags += (u & 0b10000111);
  return flags;
}

// 使用例:
var n = brailleLetterToNum("⢸");
console.log(n.toString(2));
//=> 11110000

全て取得するとこんな感じ。

var all = "";
for (var i = 0; i <= 0xFF; i++)
  all += numToBrailleLetter(i);
⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏
⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟
⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯
⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿
⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏
⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟
⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯
⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿

↑これそのままソースに書き込んで使うなら上の変換関数は不要

Booleanの配列から点字にする

var ary = [
  [true, false],
  [false, true],
  [true, false],
  [false, true]
];

上の配列をに変換したい。
左上から順番に$2^n$をかけて足せばOK。

var flags = 0;
flags += ary[0][0] * 0b00000001; // 0x01 (1)
flags += ary[1][0] * 0b00000010; // 0x02 (2)
flags += ary[2][0] * 0b00000100; // 0x04 (4)
flags += ary[3][0] * 0b00001000; // 0x08 (8)
flags += ary[0][1] * 0b00010000; // 0x10 (16)
flags += ary[1][1] * 0b00100000; // 0x20 (32)
flags += ary[2][1] * 0b01000000; // 0x40 (64)
flags += ary[3][1] * 0b10000000; // 0x80 (128)

var letter = numToBrailleLetter(flags);
//=> ⢕

連番にするために論理和の代入とビットシフトに書き換えて..

var flags = 0;
flags |= ary[0][0] << 0;
flags |= ary[1][0] << 1;
flags |= ary[2][0] << 2;
flags |= ary[3][0] << 3;
flags |= ary[0][1] << 4;
flags |= ary[1][1] << 5;
flags |= ary[2][1] << 6;
flags |= ary[3][1] << 7;

var letter = numToBrailleLetter(flags);
//=> ⢕

ループにすればすっきり。

var flags = 0;

for (var r = 0; r < 4; r++)
  for (var c = 0; c < 2; c++)
    flags |= ary[r][c] << r + c * 4;

var letter = numToBrailleLetter(flags);
//=> ⢕

関数

上のを元にして作った、配列が何行*何列でも点字に変換する関数。

Booleanの配列から点字の文字列にする関数
function boolAryToBrailleLetters(ary){
  var flagsAry = [];

  for (var r = 0; r < ary.length; r++){
    if (r % 4 == 0) flagsAry.push([]);
    for (var c = 0; c < ary[r].length; c++){
      flagsAry[r / 4 | 0][c / 2 | 0] |= ary[r][c] << r % 4 + c % 2 * 4;)
    }
  }

  return flagsAry.map(a=>a.map(n=>numToBrailleLetter(n)).join("")).join("\r\n");
}
使用例
var ary = [
  [true, true, true],
  [true, true, true],
  [true, true, true],
  [true, true, true],
  [true, true, true]
];

console.log(boolAryToBrailleLetters(ary));
// ⣿⡇
// ⠉⠁

var ary = [[true]];

console.log(boolAryToBrailleLetters(ary));
// ⠁

あとはCanvasとかから画像のRGBデータを受け取って、RGBの平均とって白黒(true/false)の2値化してこの関数に渡すだけ。

動作例:http://jsdo.it/zakuroishikuro/MNbhM

おわり。

ズレ問題

点字は全て同じ幅なので、他の文字で作ったAAと違ってズレないはず。
そう思っていた時期が私にもありました。

  • Mac・iPhoneでは完璧
  • Windowsではズレる
  • Androidではフォントが間違ってるので乱れる

結局フォントによるんだけど、点字を収録しているフォントなんてそうないし、フォントごとの個性を出しちゃいけない文字だと思うし、個人差無しのデフォのままで考えていいと思う。

Mac・iPhone

表示フォント: 「Apple Braille」 (Apple Symbolsにも一応入ってる)
Macには点字専用フォントが用意されている。
全くズレずに完璧に表示される。
iPhoneのフォント事情は知らないけどズレなかったのでMacと同じなのでは。

Windows

表示フォント: 「Segoe UI Symbol」
点字用の空白U+2800の文字幅が他の点字と違うのでズレる。無意味。
本来の実用上は問題ないのかもしれないけど、なぜ同じ幅にしてくれなかった...。

対策:U+2800をなるべく目立たなそうなで置換

Android

表示フォント:「Noto Sans Symbols」 (コメントで教えていただきました)

フォント内の一部の点字が間違っている。GitHubにIssueあり
右側にしか点が無いものが全て左寄りになってる。
つまり⠈⠐⠘⠠⠨⠰⠸⢀⢈⢐⢘⢠⢨⢰⢸⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇と同じになってる。

対策:左下に点をつけて置換
⠈⠐⠘⠠⠨⠰⠸⢀⢈⢐⢘⢠⢨⢰⢸⡀⡈⡐⡘⡠⡨⡰⡸⣀⣈⣐⣘⣠⣨⣰⣸

Linux

分かりません。教えてください。

参考

Wikipedia - Braille Patterns

点字で画像を表現するライブラリ (この記事書いたあとに見つけた)
mapscii
drawille

ちなみに@nullkal氏のトゥートで思いついた