marmooo's blog

フィード

記事のアイキャッチ画像
Ubuntu デスクトップ比較: 24.04 は波乱の予感
marmooo's blog
Ubuntu 24.04 がリリースされたので、いつも通り Ubuntuデスクトップ比較をしてみました。比較条件は 22.04 のときと一緒で、以下。22.04 の時と条件は一緒ですが、言語は明示的に英語で固定しています。これはインストーラで日本語を選べるものと、選べないものがあるからです。普通は日本語の環境を比較したい人のほうが多いと思いますが、英語表示のほうがメモリ使用量には優しいため、一部のフレーバーが有利になってしまいます。たとえば Lubuntu は日本語表示にすると 100MB くらいメモリ使用量が増えます。 VirtualBox でクリーン環境を比較検証時の最大メモリサイズは 2GB起動→安定後のメモリ使用量を free --mega で比較言語は英語悲しいお知らせとしてどのフレーバーもメモリ使用量がかなり増えました。どのフレーバーも 300MB ほど確実に増加し、MATE, Kubuntu の増加量はさらに大きいです。MATE くらいのメモリ使用量だと、最適化した Windows 11 と大差ない気がします。メモリ 4GB の PC だとそろそろ辛そうなので、これからは 8GB のほうが良いかも知れません。今のところ Lubuntu, Xubuntu が鉄板であることは変わらないですが、新しく登場した Unity が Xubuntu 並で健闘しています。 Lubuntu: 644MB〜650MB (最軽量)Xubuntu: 842MB〜857MBUbuntu Unity: 890MB〜911MBKubuntu: 980MB〜1021MBUbuntu Budgie: 1043MB〜1059MBUbuntu MATE: 1115MB〜1197MBUbuntu: 1135MB〜1262MBUbuntu Cinnamon: 1221MB〜1228MB (最重量)Ubuntu 24.04 で一番の更新点は、おそらくインストーラの更新です。最初はなんか意味あるのか? と思いましたが、Lubuntu などを日本語でインストールできるのが大きいかも。まだ初期画面が英語のほうが多いですけど、シェア獲得のために本気を出し始めたのかも知れません。GUI に不満が見つからないのも、そういった理由から細かな改善を行ったのかも知れません。 Lubuntu Xubuntu Ubu
5日前
記事のアイキャッチ画像
fontconv で ligatures font をサポートした
marmooo's blog
fontconv を大幅に更新して、ligatures font をサポートしました。ligatures font の解析の仕方は Read ligature from a font #384 が参考になります。他にも Ligatures #194 が参考になるのですが、getLigatures() は low level 実装のため、応用的な解析には使いにくい問題があります。addLigature() は参考になります。svg2ttf は空だとバグるので、その修正で多少時間が掛かりました。ligature 文字列の中でまだ登録されていない Glyph を空の Glyph として登録する利用する Glyph をリストアップして登録するGlyph に適用する ligature 文字列をリストアップするligature 文字列のインデックスを利用して ligature を登録するttf2svg の実装が ligatures font を想定していなかったので改良しました。そして機能を使いやすくするために --remove-ligatures オプションを付けました。オプションなしだと ligatures は残しますが、付けるとファイルサイズを考慮して削除します。ついでなので、さらなる最適化を考慮して以下のオプションを加えました。--ligature <str> (ligature を利用したサブセット化)--ligature-file <path> (ligature を利用したサブセット化)--name <str> (glyph-name を利用したサブセット化)--name-file <path> (glyph-name を利用したサブセット化)
6日前
記事のアイキャッチ画像
フォントを様々な形式に変換する fontconv を作った
marmooo's blog
フォントを様々な形式に変換する fontconv を作りました。.ttf, .otf, .svg, .woff, .woff2 の相互変換とサブセット化に対応しています。.eot も出力だけは対応しています。なぜ作ったかというと、フォントを Web 上で利用するための woff2 最適化に苦労したからです。私の場合は以前 ttf2svg を自作したので、ttf2svg -> svg2woff2 の2回で woff2 は最適化できるようにしていたのですが、巷のアイコンは woff2 形式でしか配布されていないことがあることに気付きました。これがなかなか強敵で、woff2 をロードして最適化した後、woff2 へ再変換できるライブラリが見つかりませんでした。具体例を上げれば Material Icons の最適化は大変で、びっくりしました。さすがにこれはおかしいなと思い、様々な形式に対応したフォントコンバーターを作りました。pyftsubset は動きますが、Python でインストールがやや面倒で、異体字に対応していない問題があります。 fontminfont-carrierfont-extractorweb-font-converter pyftsubsettdewolff/font が出てきました。拡張子ごとにパーサをフル実装しているようで凄いですが、私のおきらく実装でも選択肢が増えるのは良いんじゃないかな…。OTF からの変換が難しすぎる!!!opentype.js を使っていますが、代替になるライブラリは現状存在しないので、OTF をベースに実装することは絶対条件です。最初は、唯一きちんとした otf2ttf 実装に見えた fonteditor-core を試してみました。しかし glyph-name の情報をバッサリ切り捨ててしまう実装だったので、とてもフォント最適化には使えないとわかりました。ttf2svg を使って font→svg→ttf で変換するのが、一番仕様がわかりやすくて良いとわかりました。ただ ttf2svg の実装も色々と問題があるのがわかっていたので、良い機会なのでバッサリ書き直し、fontconv と同じオプションで動くようにしました。EOT 形式についてfonteditor-core に eot2ttf が付いていたので試してはみまし
19日前
記事のアイキャッチ画像
えもじパズルを作った
marmooo's blog
えもじパズル を作りました。完成図と見比べながらパーツを並び替えて、福笑いみたいに絵文字を作るゲームです。細部まで見比べる力を鍛えるので、特に美術や算数や漢字の学習へ応用しやすいかな。えもじパズル最近は アイコン点描写、漢字点描写 を作っていて、絵文字点描写も作っていたのですが、ボツ案となりました。絵文字は (1) 複雑すぎる、(2) 直感的な描画ではないことが多い、の2つが厳しかったです。1 は左右に絵文字を表示することで自然と表示サイズが小さくなるため、思っていたより点が密集してしまい、複雑に感じることがわかりました。スキップアルゴリズムを付ければ複雑性は緩和できますが、パーツの重なりを活かして作成されていることが多いので点の直感性とズレていることが多く、本質的な厳しさを感じました。描画の直感性は点つなぎだと誤魔化せるけど、点描写は誤魔化せないからなあ。作り方パズル部分の作り方は簡単なのですが、様々な絵文字で動くようにするのが一苦労でした。たとえば drag & drop のためにはピースごとに transform 属性を除去して新たな transform 属性を付与する必要があります。ブラウザだと階層的な transform を getCTM() で解析できるので良いですが、非ブラウザだと解析はなかなか大変で対応ライブラリはないと思います。getCTM() を使っても元々の path データに transform: matrix() があると移動用の transform 属性と混じってしまうので、まだ面倒なことあります。これは元の transform の matrix を逆変換しながら座標変換を行い (参考1、参考2)、正しい移動距離を算出し、matrix を更新する必要があります。意外と難しい。DOMMatrix や SVGMatrix、getCTM() は初めて使いましたが、なかなか便利です。やはりブラウザの API は最強です。
1ヶ月前
記事のアイキャッチ画像
drop-inline-css でインライン化レベルを最適化する
marmooo's blog
CSS 最適化ツール drop-inline-css を更新し、drop-inline-css でインライン化レベルを最適化できるようにしました。一部分だけ不要 CSS をチェックしたり、一部分だけはチェックをせずそのまま inline 化できたりします。めちゃくちゃ便利な気がしますが、まあ私だけかな…。どんなことができるなったかというと、こんな感じの HTML を、<html> <head> <link class="drop-inline-css" rel="stylesheet" href="inefficient.css"></link> <link class="inline-css" rel="stylesheet" href="efficient.css"></link> <link rel="stylesheet" href="keep.css"></link> </head> <body> <p>styled</p> </body></html><html> <head> <style>p { text-decoration: underline; }</style> <style>pre { color: red; }</style> <link rel="stylesheet" href="keep.css"></link> </head> <body> <p>styled</p> </body></html>静的な HTML から要不要が判断できる CSS動的な HTML ではあるものの事前記述で要不要が判断できる CSSJavaScript をよく見ないと要不要が判断しにくかったり設定が面倒な CSSBootstarp の CSS だけで動く大部分のコードBootstrap の JavaScript が必要なコンポーネントautocompletr や simple-keyboard などのライブラリまた上記 2-3 の CSS は遅延ロードが可能で、その最適化は 3種類に分けられます。 <link rel="stylesheet" href="base.css" media="print" onload="this.media='all';this.onload=null;">template タグ内で style を loadtempla
1ヶ月前
記事のアイキャッチ画像
アイコン点描写と漢字点描写を作った
marmooo's blog
アイコン点描写 と 漢字点描写 を作りました。アイコン点描写漢字点描写作り方上記が作り方の基本ですが、実際はもう少し必要です。たとえば点と点の隣接状態は Z/z/M/m コマンドを解析しながら行なう必要があり、重複する座標に点があったときのイベント処理を考慮する必要があります。この 2つがかなり面倒でした。アイコンの調整アイコン点つなぎ や 漢字点つなぎ と同じものが使えます。ただアイコンのほうはやや苦労しました。具体的には アイコン点描写 は解答を先に表示しているので、プロットするときと差があると面白くありません。このとき、透明な外枠を付けている Tabler Icon の違和感が大きい問題が起きました。透明な枠はプロットする必要ないですからね。アイコンセットを変えることも検討して結構色々なアイコンセットを試したのですが、透明な外枠を付けているアイコンが意外と多かったので、Tabler Icon は中身を少し弄ることで対処しました。こういう細かな調整が必要な時もあるので、アイコンセットはライセンスが緩くないと難しいなと感じました。今のところ MIT / Apache-2.0 / CC-BY / SIL-OFL-1.1 に限定しています。アイコン点つなぎ では線を引く仕組みを作るのは困難でしたが、これなら線を引く練習にも使えます。ただしスマホでプレイするのは難しいので、タブレットか PC でプレイするほうが良さそう。またほとんど同じ位置に点がある時は二回なぞらないといけないのは注意点でしょうか。こういうのをどこまでチェックするかは割と難しい問題です。
2ヶ月前
記事のアイキャッチ画像
えもじ点つなぎを作った
marmooo's blog
えもじ点つなぎ を作りました。アイコン点つなぎ はアイコンを使っているので、モノクロでシンプルなものがほとんどでした。逆に 漢字点つなぎ は難しすぎる子もいるでしょう。絵文字ならカラフルで難易度はほどほどのものが多いので、新鮮なものになります。プレイ感覚は絵を書いている感覚に近くなり、デジタルデータの仕組みを学ぶのにも使えます。えもじ点つなぎ今のところ、カラフル版は Noto Emoji, Twemoji, Fluent Emoji (Color)、Blobmoji、Emoji Two (Color)、モノクロ版は Noto Emoji Twotone, Fluent Emoji (High Contrast)、Emoji Two (Twotone) をサポートしています。初期リリースでのサポート数は 2万個 ほどです。作り方アイコン点つなぎ、漢字点つなぎ では数字の再配置アルゴリズムだけで十分に遊べるものになりましたが、絵文字は絵の複雑性に一貫性がないため、問題によっては再配置で対処できないケースがたまにあります。同じ地点を何度も通ったり、局所的に複雑なケースがあり得るということですね。再配置で対処できない点を一時的に消すのが楽そうと思いましたが、局所的に問題が発生したときにわかりにくいし、紙に印刷できないのが気になりました。そこで密集度の高すぎる領域をスキップする実装を取り入れました。
3ヶ月前
記事のアイキャッチ画像
漢字点つなぎを作った
marmooo's blog
漢字点つなぎを作りました。書き順は考慮する必要がないのでいい感じのフォントさえあれば作れます。とはいえそこがハードルで、思ったより考えることがありました。漢字点つなぎフォント選びNoto Serif JP 以外に良いフォントがないか、改めて色々なフォントを見ました。シンプルで良いなあと思うのはゴシック形式の Noto Sans JP で、これはサポートしておきました。ただ Noto 以外で点つなぎに使えそうなもの、と考えるとなかなかちょうど良いものはない気がしています。ttf2svg@marmooo/ttf2svg を使っていますが、細かな調整が必要になったので、ついでに改良しておきました。具体的には、文字を仕様上表示され得る領域にSVG化するのと、ぴったり拡大して最大領域に SVG 化するのは微妙に違い、対応しにくい問題があったので、細かな調整ができるようにしました。たいていの絵文字やフォントは advancedWidth と linespace = ascent - descent + linegap を見れば、SVG の viewport を決定できます。ただ漢字にこの条件を当てはめると、特に descent の影響が大きすぎて不自然な余白が生まれます。とはいえ descent だけを無視する訳にもいきません。なぜならフォントの作者によって、ascent, descent はバラバラの意味を持っているからです。そこで units-per-em を使うと、ぴったりになることが多いです。ただ units-per-em も壊れたデータが入力されていたり、縦横比が壊れていると意味をなさなかったりします。仕方ないのでオプションで数字調整できるようにしました。そんな訳でサポートしているのは約 1万1000種類×2=2万2000個のグリフです。これだけあれば量で困ることはないでしょう。アイコン点つなぎと違ってそれなりに歯ごたえがあります。
4ヶ月前
記事のアイキャッチ画像
アイコン点つなぎを作った
marmooo's blog
アイコン点つなぎ を作りました。アイコン点つなぎ幼児教育では点つなぎやグリッド点つなぎが有名です。それらは線を書く練習や、簡易的に認識力を向上させる練習として便利ですが、題材を作ったり探すのが大変です。そこで自動生成してゲーム化すると面白そうだなと思って作りました。難しいところ作り方svgpath を使って配列の座標にします。path 配列から text で数字を表示します。あとは text を順番にタッチしたときに path の線を復元するようなコードを書けば完成です。アイコンにも色々あるアイコンセットによっては、稀に外枠と内枠を一つの線で書いているもの (Line Icon?) があります。これが世の中の点つなぎに一番近いもので重要と考えています。ただそのような書き方をしているアイコンセットは多くありません。また地道にソースコードを見る以外で見分けが付かないので確認が大変です。今のところ Line Icon として使えて数が多いものは Iconoir、Solar icon set、Majesticons、Lucide くらいと思っています。これらは優先的にサポートしました。Material Line Icons も使えそうでしたが、animateMotion タグの解析が大変そうだったので見送りました。
4ヶ月前
記事のアイキャッチ画像
Unicode の部首を一意にする
marmooo's blog
@marmooo/kanji の部首データを Unicode 全漢字に対応させました。@marmooo/kanji では画数データは一意になるように設計しているのですが、Unihan Database の部首は Unicode 15.1 では 183 件の重複登録があり、この解決に苦労が伴ったのでメモを残しておきます。ちなみに Unihan Database では画数の管理で int を諦めて string を使っているのですが、情報が不確かな漢字のために int を放棄するのは嫌なので int にしています。具体的なコードはこちらを参照してください。漢字データベースプロジェクトのようなチェック が必要になるのでしょうね。将来的にはやってみるかも知れませんが、IDS も更新しないといけないので時間が掛かります。同じように中国では簡体字を作ったときのことをよく考えないといけない漢字が多々あります。たとえば日本だと「卧」はそのままの字形で理解されていて Unihan Database では「卜部」です。ただ中国だと「臥」が元字で、簡化によって「卧」になったので、康煕字典に合わせた部首は「臣部」なんですよね。康煕字典に記載があるからといって、合わせすぎるのも難しそうです。新しい漢字と古い漢字は分けて考えないといけません。「厼」のように康熙字典網にも文字情報基盤にも登録されていない漢字も、データがどこから来たのかよくわからないで困ります。ネットで調べてもほぼ情報がなく、なぜか一番詳しいのが 日本語の Wiktionary みたいなケースがチラホラあります。当然ながら部首がわかるはずもないので、Unicode に従うしか方法はないでしょう。ちなみに「厼」の本字は「彌」のようで、そこから様々な異体字と新字体が生まれているようです。異体字はどれも部首がバラバラでちょっと笑えます。文字を簡単にしようとすると部首が崩壊するんだなあ。つまり本字以外の部首は深く考えたら負けで、特徴的なパーツを選び、Unicode 配列に従っておけば良さそうです。また本字以外は部首より、異体字をたどったり、IDS で検索するほうが良さそう。追記: Unicode® Standard Annex #38 の画数の説明 を読んでいると、一意にしようとはしているみたいですね。あまり手を出さないようにしつつなるべ
4ヶ月前
記事のアイキャッチ画像
SVG の描画要素を path に変換する shape2path を作った
marmooo's blog
SVG の描画要素 (rectangle, circle, ellipse, line, polyline, polygon) を path に変換する shape2path を作りました。以下の類似ライブラリが存在することはわかっているのですが、Web 上で動かなかったり、実装不足があったり、XML パーサが巨大すぎるので、依存なしでシンプルに動くものを作りました。依存がキツかったり fs module 依存が邪魔なときに毎回ゼロから作り直しているのですが、どうするのが良いのかなあ。たぶんどうしようもないので、Node.js / Deno / Bun に依存するコードはなるべく使わないのが鉄則でしょうね。 svgo convertPathSVGPathConvertersvg-flattenブラウザとオフラインの実装を分けるのが難しいすべてを諦めて DOM を再現するライブラリを使うのが楽ではあるのですが、バンドルサイズは大きくなります。この路線でいくなら linkeDOM は互換性が高く、速度もまあまあなので良さそうです。とはいえ 300KB くらい増えてしまうので依存性をゼロにしたいなと思い、HTMLDocument は無理やり使わないようにして、関連するオブジェクトとメソッドを引数で渡す実装にしました。これなら依存性が引数として与えられる外部の HTMLElement だけになり、かなり動かしやすくなります。ただこの路線での実装は、ブラウザと完璧な互換性を持っているライブラリがほとんどない問題があります。たとえば私がよく使っている node-html-parser は HTMLElement.cloneNode() をサポートしていないので、多少ながら違和感が残る実装になりました。ただこういう違和感はブラウザに慣れ過ぎているからで、ESM 的な観点から考えると普通なのかも知れません。DOM の API を使ったライブラリは作り方が難しいですね。円/楕円の変換方式SVG の <circle> を <path> で描く を利用しています。描画パターンを選択できると嬉しいと思ったので、すべてサポートしています。私のお気に入りは「8本の2次ベジェ曲線で描画」です。描画精度は低い反面、応用性が高いです。
4ヶ月前
記事のアイキャッチ画像
都道府県タイピングを作った
marmooo's blog
都道府県タイピングを作りました。似たようなものはいくつかありますが、やはり自分用のものが欲しかったので車輪の再発明をしました。都道府県タイピング何分くらいでできるかなと思って試してみたところ、5.0 key/sec だと 1分くらいでクリアできるみたいです。考慮時間を含めると 100秒くらいでしょうか。小学 4 年生〜小学 6年生の子が遊ぶことを想定すると、早い子が 3.0 key/sec くらいと予想できます。1.5〜3.0 key/sec の子ならクリアできるように 5分間のゲームとして作りました。1.0 key/sec 以下はもう少しタイピングの練習をする必要があります。遊んでみると、意外と文字数が少ないので初期の学習には良いかも。ただこの手のタイピング、昔からどうにも HARD モードは苦手なんだよなあ。たぶん私は言語中枢をメインに使って都道府県を覚えている訳じゃないんだろうな。
5ヶ月前
記事のアイキャッチ画像
漢字の情報取得ライブラリ kanji を作った
marmooo's blog
今さらですが、欲しくなったので漢字の情報取得ライブラリ @marmooo/kanji を作りました。漢字の情報と言っても色々ある訳ですが、レベルごとの漢字のリストを取得し、文章のレベルを取得するライブラリです。特に教育指導要領をチェックするコードは何十回も書いているので、そろそろライブラリが欲しいと思って自作しました。ついでにかなり色々な機能を付けたので、つよつよです。漢字の処理は非常に難しいので、このような誰も作らない基盤ライブラリの整備が必要です。import { Kanji, JKAT } from "@marmooo/kanji";const kanji = new Kanji(list);const jkat = new Kanji(JKAT); // 教育指導要領 / 日本漢字能力検定jkat.getGrade("学校"); // --> 0jkat.getGrade("漢"); // --> 2jkat.getGrade("うどん"); // --> -1JISCode: JIS 漢字コード (第 1 水準〜第 4 水準)Jinmei: 人名用漢字 (常用漢字, 常用漢字の異体字, 人名用漢字)JKAT: 教育指導要領 / 日本漢字能力検定 (10級〜1級)Unicode1Radical: Unicode 1.1 の康熙字典 214 部首コードUnicode1RadicalStrokes: Unicode 1.1 の康熙字典 214 部首の画数データJoyoRadical: 常用漢字の康熙字典 214 部首コードJoyoRadicalStrokes: 常用漢字の康熙字典 214 部首の画数データJoyoStrokes: 常用漢字の画数データJIS4UnihanStrokes: JIS 第 4 水準の Unihan_IRGSources.txt に基づく画数データ漢字の画数を調査した漢字の部首を調査したライブラリ化するときにはとにかく扱いの難しい漢字として「𠮟」があります。「𠮟」は新しい常用漢字ですが、[音訓の小・中・高等学校段階別割り振り表(平成29年3月)]を見ると、「𠮟」じゃなくて「叱」で書かれているんですよねえ。漢字検定の漢字一覧も表記は「𠮟」なのですがコピペすると「叱」になったりして困ります。他にも間違いがいくつかあって紛らわしい。そう...
5ヶ月前
記事のアイキャッチ画像
漢字の画数を調査した
marmooo's blog
こども漢字辞書 を改善するために、漢字の画数を調査しました。使えるデータの中でどれが一番信用できるか常用漢字のリストをチェックしたところ、以下になりました。特に酷いのが UnihanXML で、2136 文字中、2052 文字ずれています。もはや合っているほうが少ない。これはひどい。$ deno run -A check-stroks.jscheck 2136 kanjis:diff animCJK: 1diff joyoKanji: 5diff mojikiban: 5diff cjkvi: 145diff unihanIRGSources: 177diff unihanXML: 2052KanjiVG と animCJK が一番正確です。joyoKanji は古い常用漢字しか対応していないため 4つ間違いが増えていますが、データとしては完璧です。MJ 文字 も良い精度ですが、フォーマットが地獄という問題があります。それ以外の画数データは使えません。ちなみに KanjiVG, animCJK, joyoKanji で画数が異なる文字は一つだけしかありません。それが「衷」で、9画と 10画のどちらが正しいのかという問題があります。結論から言えば どちらでもいい ようですが、学校を基準にするなら 9画が正しいようです。@marmooo/kanji にも常用漢字の画数データを追加しました。以下のように画数のレベル情報を取得できます。import { Kanji, JoyoStrokes } from "@marmooo/kanji";const joyoStrokes = new Kanji(JoyoStrokes);joyoStrokes.getGrade("学"); // --> 8joyoStrokes.getGrade("校"); // --> 10joyoStrokes.getGrade("学校"); // --> 10常用外漢字kanji_kakusuu は人名用漢字もサポートしているみたいです。人名用漢字以上を無理やりサポートするなら unihan_IRGSources.txt を改良した cjkvi が良さそうですが、古いですしミスは必ずあります。@marmooo/kanji でサポートしています。unihan_IRGSources.txt を使えば J
5ヶ月前
記事のアイキャッチ画像
漢字の部首を調査した
marmooo's blog
こども漢字辞書 を改善するために、漢字の部首を調査しました。MJ 文字、KanjiVG、JoyoKanji の 3つがありますが、厳密なチェックをしたことはありませんでした。部首は表記の仕方がバラバラなのでチェックをするのが思ったより大変でした。部首の表記ゆれ他にも黒部を示す部首に「黑 黒」のどちらを使うかという問題もあります。これも 日本の新字体は「黒」のように囲いの内部を「十」としている。なお、表外漢字においては康熙字典に従い「黑」を用いる。 のが正解です。あと「内」くらいの簡単な漢字でさえ「入部」と「冂部」のどちらもあり得たりして、部首そのものが適当すぎるので困ります。そんなとき、たいていのネット辞書は文献が不明であまり信頼性がないという問題が起きるのですが、KanjiVG は JIS漢字字典を利用しているという明確な信頼性があります。やはり少なくともネットの部首情報よりは KanjiVG のほうが信頼できるというのが現状ではないでしょうか。部首の読みの表記ゆれ偏旁 を考慮して自動で名前を付けられないかと一瞬思いましたが、例外が多すぎてたぶん無理です。特にいい方法はないので、康熙字典 214 部首に位置情報を付けたデータを用意し、読みを返す関数を作りました。実際に作ってみると、やはり「人部」などのコンポーネント情報だけだと、部首名を決めるのは難しいことがわかりました。たとえば「从」は部首の位置自体は左にあるはずですが、どう見ても「亻 にんべん」ではないので、「ひと」が正しいと思います。つまり「人 亻」をきちんと分けて登録していないと、分類できないとわかります。KanjiVG 以外の部首データは不完全ということになります。これは「沖」から派生した漢字で、kvg:element「冫」の意味する氷とは関連がないので、kvg:originalが「水」になっているようです。英語のほうが日本語より正確というね…。しかしこの登録は紛らわしいだけのような気もします。とりあえず部首を考えるだけなら kvg:element だけを見るのが安全そうで、kvg:original は見ないほうが良さそうです。月部と肉部の見分けくらいには使えますが、間違いが多すぎる問題があります。難しスギィ!CJK 漢字の部首情報CJK 統合漢字を漢字配列だけで康熙字典 214 部首に変換している人もい
5ヶ月前
記事のアイキャッチ画像
漢字の音訓辞書 Onkun をつよつよにした
marmooo's blog
音訓辞書 Onkun をつよつよにしました。常用漢字の教育用の音読み・訓読みのデータは政府が 2017年、2010年に公開しており、2010年はまだ楽だったので対応していました。2017年は小中高ごとに履修すべき項目が用意されている一方、パース不能のため放置していました。いつかはやらないとと思っていたので手作業で分類し、送り仮名も付けて、ライブラリ化しました。常用漢字以外も一応、対応しています。常用漢字以外の音訓データとして、たとえば今は亡き MJ 文字 にも音読み・訓読みデータがありますが、送り仮名の情報はありません。他にも後述するようなリソースが色々あるにはあるのですが、送り仮名をどうするかがネックになりがちです。またリソースが分散していて面倒すぎる問題があります。Onkun では送り仮名問題は残っているのですが、様々なリソースをまとめて、簡単に処理できるようにしました。以下のように使えます。昔のバージョンとは互換性がないですが、まあ仕方ない。import { Onkun } from "onkun";const onkun = new Onkun();await onkun.loadJoyo("data/joyo-2017.csv");await onkun.load("Joyo", "data/joyo-2010.csv");await onkun.load("Unihan", "data/Unihan-2023-07-15.csv");onkun.get("漢"); // --> { 小学: ["カン"], 中学: [], 高校: [], Joyo: ["カン"], Unihan: ["カン", "タン", "から"] }辞書についてjoyoKanji というオープンデータがあって、たぶんこれが一番使いやすいです。Onkun はこのデータとの整合性チェックは行っているので、登録ミスはたぶんないと思う。常用漢字以外については Unihan Database に頼るのが良さそうですが、送り仮名はありませんし精度はそこまで信用できません。KANJIDIC があります。送り仮名や英訳が付いている時もあり利点ですが、精度はほどほどです。13,000 字くらいしか対応しておらず、ライセンスが面倒なので、今回は見送りました。MJ 文字 も 6000字くらいしか対応してお
5ヶ月前
記事のアイキャッチ画像
世界地図パズルを作った
marmooo's blog
Web 上で遊べる世界地図パズルを作ってみました。普通の世界地図パズルと違って、なんと 12 レベルもあります。レベル 9 くらいまでは子供向きで簡単ですが、レベル 10 になると大人でも難しいです。世界地図パズルどの SVG が良い?File:BlankMap-World-Flattened.svg を利用しています。 File:BlankMap-World-Flattened.svgFile:World map with nations.svgFile:World location map.svgsvg-maps - World Map球体状に歪んでいない南極・北極はあったほうがいいなるべく特徴は残っているほうがいいpan/zoomHTML 要素の pan/zoom の実装はひさびさにものすごくハマったので、メモも残しておきました。Pinch to Zoom all Canvas Objects #2658 が参考になります。DOM の実装は、マルチタッチに注意するべきところはたくさんあるものの、TouchEvent に 2本指の距離を測定する値 scale がある ので、scale の変化を見ながら zoom すれば割と簡単に実装できます。といってもマルチタッチを考慮すると色々考えることがあり、やはり大変でした。あまりにつらかったので、もう pan/zoom, pinch zoom は実装したくない…。バチカン市国?File:BlankMap-World-Flattened.svg には面積の小さな国がアノテーションされているので数えてみたところ、約 150 カ国以外は強調表示しないと地図上で確認しにくい大きさと定義されていました。ちなみにくもんの世界地図パズルはさらに少なく 85ピースです。国の面積順リスト と実際の地図を比べてみると、キルギス (85位) は割と大きい国です。85位までに限定すると韓国、北朝鮮、ネパール、ブルガリア、キューバなどがなくなってしまうことが気になりました。特に韓国は面積が小さくても経済大国なので、消してしまうのは世界を認識するためのパズルとしてはちょっとよろしくないかも。ただいくら頑張ってもシンガポールはどうしようもないので、限界はあります。File:BlankMap-World-Flattened.svg の定義をそのまま使う
6ヶ月前
記事のアイキャッチ画像
HTML 要素の panzoom
marmooo's blog
世界地図パズル を作った時、HTML 要素の panzoom が必要になりました。有名なものとしては以下があり、すべて検証したのですが、それぞれ欠点があることがわかりました。もうハマりたくないのでメモを残しておきます。 svg-pan-zoomtimmywil/panzoomanvaka/panzoomsvg-pan-zoom は (1) ホイール時にページも動く、(2) object タグの中に適用してしまうので予期しない動作が生まれる (特に y軸方向)、anvaka/panzoom は(3) z-index が変わって表示に影響が出る、(4) フォーカスが時々外れて違和感がある、(5) 特定範囲内で動かす処理が指定しにくい、(6) zoomTo() で縮小できない致命的な問題がある、timmywil/panzoom は(7) イベントが増えると zoom がズレる、などが気になりました。1 は親要素に overflow: hidden; を付けることで解決できるとわかったのですが、3, 4 は不明です。7 が見つかるまでは timmywil/panzoom が一番良さそうだったのですが、この問題が厳しかったので、結局はセルフ実装をするしかありませんでした。Canvas の panzoom はつらい世界地図パズル では panzoom の実装を 200行くらいで実現していますが、作るのはものすごく時間が掛かった気がします。ちなみに何を苦労したかというと、1つは Canvas ライブラリ fabric.js との擦り合わせです。まずは Canvas だけで実装してみようと思ったのですが、その場合 setBackgroundImage() で地図を背景画像にし、zoom を実装することになります。しかし拡大していくと再レンダリングされない問題が発生し、ジャギーが気になりました。小さな SVG はきちんと再描画されているので、巨大な画像だけ別処理の扱いなのかな。issue で同じ問題が提起されており、現状この解決がかなり難しいとわかりました。panzoom を実装するときは SVG などを使ったほうがずっと簡単です。timmywil/panzoom の両方を使って実装しました。しかし座標変換の方法に差異があり擦り合わせに苦労し、また 7 の問題に直面し、セルフ実装を行
6ヶ月前
記事のアイキャッチ画像
世界各国の地図パズルを作った
marmooo's blog
Web 上で遊べる世界の国の地図パズルを作ってみました。世界で発売されている地図パズルに合わせて、既に作ってあった日本版の他に、アメリカ・中国・インド版を作りました。普通の地図パズルと違って、なんと 12 レベルもあります。レベル 9 くらいまでは子供向きで簡単ですが、レベル 10 になると大人でも難しいです。日本地図パズルアメリカ地図パズル中国地図パズルインド地図パズルインド地図パズルは機械翻訳などを使ってヒンディー語に対応すべきか迷いましたが、今のところしていません。Amazon で売られているものも英語で書かれているから、なくても大丈夫かな? という判断です。ヒンディー語のパズルがない理由は、おそらく英語で学ぶ層の子にだけ需要があるからと思っています。ヒンディー語やウルドゥー語で学ぶ層の子にはそれほど必要ないのかなと予想して今回は作りませんでしたが、必要とあらば実装するつもりです。代わりに世界地図パズルを作っているので、そのうち公開します。
7ヶ月前
記事のアイキャッチ画像
Deno で readLinesSync() を作った
marmooo's blog
Deno / Node.js にはファイルから非同期処理で 1行ずつ読み込む readLines() があります。昔よりだいぶ楽になったなあと思うのですが、実行速度がすこし遅いので、同期処理で 1行ずつ読み込む readLinesSync() を作りました。Deno ならこんな感じで使えます。Node.js / Bun ではバイナリを扱ったことがなかったので気付かなかったのですが、dnt 経由で使うか、Uint8Array でブロックごとに読み込むコードを書く必要がありそうです。import { readLinesSync } from "https://raw.githubusercontent.com/marmooo/readlines-sync/main/mod.js";const file = await Deno.open("FILE");for (const line of readLinesSync(file)) { console.log(line);}file.close();ベンチマーク結果は以下です。最後の 3つが自作したもので、sync1 が一番最初に実装したもの、sync2 はキャッシュのヒット率を考慮したもの、readLinesSync は sync2 を Deno / Node の API に近づけた完成物です。Node.js の readLines() もベンチマークに入れたかったですが、まだ Deno からは使えないのでパス。ただ node:readline と同じ速度と予想はできます。cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHzruntime: deno 1.36.4 (x86_64-unknown-linux-gnu)bench.jsbenchmark time (avg) iter/s (min … max) p75 p99 p995--------------------------------------------------------------------- -----------------------------node:readline 1.34 s/iter 0.7 (1.29 s … 1.44 s) 1.36 s 1.44 s 1.44 snpm:n-read
8ヶ月前
記事のアイキャッチ画像
ローマ字の解析ライブラリ romaji を作った
marmooo's blog
ローマ字の解析ライブラリ @marmooo/romaji を作りました。主にタイピングの問題文として与えるひらがなをローマ字に変換したり、どこまで入力したかの確認、といった利用を想定しています。タイピングのアプリは割とたくさん作ってきた割に、ローマ字入力のタイピングはクソコードになっていたので、完璧にしました。実装例import { Romaji } from "@marmooo/romaji";const problem = "がっこう";const romaji = new Romaji(problem);romaji.input("g"); // --> trueromaji.input("j"); // --> falseromaji.input("a"); // --> trueromaji.inputedRomaji; // --> "ga"romaji.inputedHiragana; // --> "が"globalThis.addEventListener("keydown", (event) => { if (romaji.input(event.key)) { if (romaji.isEnd()) { nextProblem(); } else { correctType(); } } else { incorrectType(); }});hiraroma を作ったのですが、仕組みは異なります。一般的なひらがな→ローマ字の変換では最もよく使われるヘボン式さえサポートしていれば十分です。しかしローマ字入力のタイピングではすべての変換形式に対応することが求められます。この対応は思っているより面倒で、これまで完璧に対応しているものはなかったと思います。最近ようやくこの手のライブラリが増えてきた気がしますが、実装面で納得のいくライブラリはなかったので、その辺りを軽く説明します。タイピング用のデータ構造 (基本)ローマ字入力でパターンが複雑になる条件は、実はたった 1つしかありません。前に「っ」、後ろに「ぁぃぅぇぉゃゅょ」が来た時だけです。つまり「が|っこ|う」「や|きゅ|う」「が|っきゅ|う」となるように正規表現でセグメント分割すれば、ただの配列として管理できます。あとはひらがなの部分文字列 (セグメント) からローマ字のパターンを生成するだけです
9ヶ月前
記事のアイキャッチ画像
Deno のコマンド実行を改めて考える
marmooo's blog
Deno でコマンド実行したいと思った時、zx を使うと簡単です。しかし zx にもいくつか実装の選択肢があり、微妙に仕様が違って毎回迷うので、まとめておきます。 dzxbazxdaxzx-denodeno-dxzx (本家)stdout, stderrr の調整zx-deno と bazx は stdout, stderr の調整がしにくいので、複雑なコマンドになるとやや使いにくいです。コーテーションの自動挿入dzx と dax はコーテーションを自動で挿入します。await $`sd -f m "${from}" "${to}" ${files.join(" ")}`dzx だとたぶん実行できません。dax は以下のように書けます。await $`sd -f m ${from} ${to} ${files}`;コマンド置換await $`sd -s "${from}" "${to}" $(fdfind --type file -e html .)`;dzx, bazx, dax は動きません。ただ bash の機能を使わずにスペース区切りで展開したほうが依存が少なくて良いかも知れません。書き換えると以下になるでしょうか。ダブルコーテーションを付けるのが面倒ですね。あと何気なく "${from}" と書いていますが、from の中にダブルコーテーションがあった時に失敗する問題があります。const targets = await $`fdfind --type file -e js -e html .`;const files = targets.split("\n").map((target) => `"${target}"`).join(" ");await $`sd -s "${from}" "${to}" ${files}`;dax ならこの処理は自動でやってくれるので、以下のように書けます。${from} 部分を自動でエスケープしてくれるので、ダブルコーテーションのバグが発生しにくいのも良いところです。コマンド数が増えると楽になるかも知れません。ミスを自動で排除できる点では dax が優れていると思います。const files = await $`fdfind --type file -e js -e html .`.lines();await $`s
9ヶ月前
記事のアイキャッチ画像
日本地図パズルを作った
marmooo's blog
Web 上で遊べる都道府県パズルを作ってみました。普通の都道府県パズルと違って、なんと 12 レベルもあります。レベル 9 くらいまでは子供向きで簡単ですが、レベル 10 になると大人でも難しいです。日本地図パズル最終的には割と良いものができたと思うのですが、意外と事前準備に苦労しました。都道府県の SVG が難しいちなみに 47都道府県のポリゴンデータは国土数値情報にあって、Geojson もあって、軽量化もできる ことはわかっています。OpenStreetMap Region なども使えるのではないかと思います。真面目に作るならそれらを SVG 化したほうがライセンス的にもデータ的にも良いのですが、とにかく面倒です。svg-maps はそれなりに使いやすいですが、北方領土が載ってないため歴史や地理の説明では使いにくい問題があったり、韓国のウルルン郡がなぜか日本地図に入っていたり、与那国島がなかったり、離島がたくさん消失していたり、鹿児島県と沖縄県の県境が間違っていたりします。このように細かな領域ではたくさんの問題があって、教育用途での利用は少々厳しい印象があります。geolonia/japanese-prefectures だと思います。ただこれも細かい部分は調整しないと使えないので、調整した marmooo/japanese-prefectures を作って公開しました。しかしこれも改めて見てみると、湖がほとんど書かれていないのはわずかな不満点です。Wikipedia の Japan Map Lincun.svg などを使って今後作り直すかもしれません。困ったら Wikipedia から拝借するのが、結局のところ一番良さそうです。Resizable, Rotatable, Draggable をどう作る?Moveable とSubjx しかないと思います。実質的に Moveable 一択なんじゃないかな。Moveable は使ってもみたのですが、処理がちょっと重いです。理由は 8つの点と 4つの線で移動用の矩形オブジェクトを作っているのですが、その描画に position: absolute; を使っているからです。普通に考えれば動かしたいオブジェクトに wrapper として矩形オブジェクトを付ければ position:relative; で表現でき、座標情
10ヶ月前
記事のアイキャッチ画像
Worker Module + TensorFlow.js
marmooo's blog
Firefox 114 で Web Worker の ESM (Worker Module) がサポートされました。この更新によって Web Worker 内でバンドルサイズの最適化がしやすくなりました。importScripts("https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js");let model;(async () => { model = await tf.loadGraphModel("model/model.json");})();const worker = new Worker("worker.js", { type: "module" });import * as tf from "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";import "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";import { loadGraphModel } from "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";let model;(async () => { model = await loadGraphModel("model/model.json");})();import { browser, cast, div, expandDims, scalar, tidy, setBackend,} from "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";import "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";import { loadGraphModel } from "https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/+esm";let model;(as
10ヶ月前
記事のアイキャッチ画像
object タグ経由で SVG の読み込み完了をチェックする
marmooo's blog
object タグ経由で SVG の読み込み完了をチェックする方法をまとめておきます。様々なケースで利用できますが、特にダークモード対応で便利かと思います。CSS の filter を使うなどの方法があるかも知れませんが、普通はそんな超絶技巧はしないほうが良いです。<object type="image/svg+xml" data="test.svg" width="64" height="64"></object>この問題に関して、StackOverflow からヒントが得られました。そのままだと使いにくいので、使いやすく書き直したものが以下です。HTML 側で data 属性を事前に書いていると、後から JavaScript 側で onload 属性を付与してチェックしても、既に読み込み完了になっている可能性があります。そこで getCurrentTime() を使うことで、SVG が読み込み完了かどうかチェックできます。function isLoaded(object) { const doc = object.contentDocument; if (!doc) return false; const svg = doc.querySelector("svg"); if (!svg) return false; if (svg.getCurrentTime() この関数を使って object タグで読み込んだ SVG をダークモード対応する時は、たとえば以下のように実装します。具体例 (タッチde書き順):if (isLoaded(object)) { const svg = object.contentDocument.documentElement; svg.style.background = "#212529"; svg.firstElementChild.style.stroke = "#fff";} else { object.onload = () => { const svg = object.contentDocument.documentElement; svg.style.background = "#212529"; svg.firstElementChild.style.stroke = "#fff"; };}今までは お手軽にダー
10ヶ月前