kotlitexの複数行対応
線形計画法は数式を複数縦に並べる必要がある。 これを手元のノートとweb上でいい感じに表示するには、素直に複数行対応する方が良さそうな気がする。
だが現状は数式はTextViewのSpanの一種として実装してあって、高さが可変な場合の扱いはあまり真面目に考えてない。 という事で結構大変なのだが、これを解決しないと線形計画法の教科書の続きを読む気が起きないので、この連休でやる事にする。雨が三日続くらしいし。
初日の進捗
とりあえずKaTeX移植部分から。
バックスラッシュ2つはマクロとして実装されているので、いい機会だからマクロも移植した。 もともと今動いている部分でも半分くらいはマクロのコードを残して移植してあったので、完全に一から移植する訳でも無いが、それなりの分量である。 さらに改行周辺の処理はbuildExpressionより外側のbuildHTMLで行われていてこの辺は移植してなかったのでその周辺も移植した。 for文でのインデクスをこっそり中で進めたり戻したりするコードがあって、 kotlin的には非常に書きにくくて苦戦したが、心を無にしてwhile文化して先に進む。
そしてレンダリング回りの処理も追加して一応動いているのを確認。 ただこのままではSpanの高さがおかしくて下の行にめり込んでいる。 これは予想通りではあるがなんでかは良く分かってない所。 明日はここをちゃんと考え直す所からかなぁ。
残るTODOとしては、
- MathSpanの高さをちゃんと合わせる
- MarkdownView側のパーサーをちゃんと複数行対応する
の二つくらい。 どちらもそんなに重くは無いので、ここまでくれば明日中には終わるだろう。
移植のコードの良しあし
このコードをここまで本格的にいじるのは随分久しぶりだ。半年ぶりくらいか? 移植のプロジェクトで、短期で終わる類の物じゃない長いものの場合、 途中の様々な選択の功罪は、こういう少しあとになったシチュエーションで明らかになると思う。
久しぶりに触った時に把握するのに時間がかかるのは良く無い部分だ。 クラス名やファイル名、どこに何を置いたか、オリジナルのJSのコードとどこを変えてどこは揃えたのか、 こうした選択の結果が、ちゃんと時間があいて触る時の事を考えて、それを助けるものになっているのが良い選択だ。 オリジナルと変えた場合に対応関係は時間がたってもすぐに把握できるか? 移植元のコードで見ているものの該当箇所をすぐ移植先のコードで見つけられるか?反対も出来るか? コメントで対応関係や変えた理由などを書いておくのは非常に助けになる。
一時的にダミーの実装にしておいた所が新しい処理を入れた時に根深い悪さをするようなのは悪い選択だ。 一方でダミーにしていた所が悪さをした時に、すぐにその事が分かるならそんなに悪くない。
コメントアウトした時に、それが一時的なコメントアウトであとで実装しなきゃいけないものなのか、 何かの理由でAndroid版では必要ないコードという事を表しているのか、 オリジナルのコードにあったコメントがコピペされただけなのか。 こうした区別がすぐにつくのは良いコメントアウトだ。
未実装の所を実装しよう、と思って実装を開始した時に、ぱっと見で実装しなきゃいけない所だけを実装して動くのは良い未実装の残し方だ。 逆にぱっと見はやらなくても良さそうに見える所に多く未実装が散らばっていて、それを把握するのが困難なら、 その残し方はあまりうまく無い。 この辺はとりあえず動くという最初の目標とトレードオフがあり、 その選択にはセンスが問われる。
ほかにもサンプルアプリやUnit TestやCIやlintがどのくらいこの久しぶりの作業を助けてくれるか、なども、 それらの選択が正解だったか良く無かったかを教えてくれる。
これらの結論は、一般に言われている正しい事よりも一段強い。なぜならよりプロジェクトにspecificだから。 よりspecificな結論の方がより実態に沿っている。 もっと言えばこれらspecificな良しあしの判断に合致しているかどうかで一般論の正しさを評価すべきくらいだ(もちろんどれか一つのプロジェクトじゃなくてそれらのspecificな成否を集約したもので)。
今回の移植は、JS+flow+HTMLからAndroid+Kotlinへの移植という相当難度の高いものであるが、 それを思えばかなりうまくやっているんじゃないか。
ascentなどの指定を直すべく格闘
以前適当に合わせようとしたが合わなかったascent回りのサイズの申告をちゃんと直す事にする。
まず、もともとデバッグ用にバウンディングボックスを表示するコードが入っていたが、これが明らかにバグっていてコメントでもなんかおかしいって書いてあるのを直す。 起こっている事の表示をちゃんと直すと、勘違いやずれている所がちゃんとわかるようになって、一気に理解が進む。 ベースの地点が0の場合(drawTextなど)の場合と負のascent分の場合(drawRectなど)が混乱している事があるのだな。
本来はバウンディングボックスの一番上をゼロにしたい所だが、インラインの時に他のテキストとベースがそろってないとみてて気分が悪い。 spanとしてはベースが確定するのはdrawの時で、どこをbaseに書くべきかは計算せずに分かるので、やはりdraw時はtextの起点が0の方が素直にも思う。
という事で負のascentが必要な物ではアドホックにちょうど合いそうな値を引く、というコードを入れる。 あまりfont sizeの変更などにrobustではないが、とりあえず一つのfont sizeできっちり動くコードを作れば、他のサイズに対応していくのはそう難しくないだろう、という事で。
仮想サイズがおかしい
なんかインラインの時とそうでない時で引くべきベースが一致しないな。これは場合分けが要るという事か。 この辺が良く分からずに前回どうやっても合わせられなかったのかなぁ。
現状は仮想的なfont size 100で全部をレイアウトして、描画する時に実際のサイズにマップする、というコードになっているが、これがバグっている気がする。たぶんfont metricsとる時は実際の高さになっている気がするな。 もともとViewとして作った時にテキストのサイズという概念が無かったのでこんな仕様にしてあったのだが、現在はspanの実装になっているから最初からテキストのサイズは分かっている。この機能は無くそう。
複数VListRow問題
インラインかどうか、という問題じゃなくて、どうも数式によってどれだけ上にずらすかが違う。 いろいろ見ていると、ロジカルには真ん中の行のベースを基準に、バウンディングボックスの上までをascentやTopとして指定すべきな気がする。 だが複数行の時には一行目のベースから上までがascentやTopになるべき。 このベースにとなる数字は数式によって違いそうなので、どこかから取得する必要がありそう。 どこかにないとレンダリング出来ないはずだからどこかにはあるのだろうが。 たぶんトップレベルのVListの一つ目のベース、とかだろうか。
また、バウンディングボックスが足りてないケースがある。これはバグっぽいな。fracの時に高さが足りてない。 これは別件のバグとしてissueを立てて、他を直していこう。
という事で複数VListRowがある時は一つ目の高さを基準にascentを決めるようにする。
負のyの時の対応(単なるワークアラウンド)
シグマで上に文字が載っている時とかに、どうも上に行き過ぎる。 なんかこの場合、バウンディングボックスのyが負の値だ。 この場合のrectのマージがおかしい気がするんだが、いろいろいじると何が悪いか分からなくなるので、 とりあえずこの場合にアドホックに下側も伸ばして行が重ならないようにだけしておく。
いい加減に足しただけの割にはなんかどの場合でもうまく動いているな。偶然正解っぽい処理なのかもしれない。まぁいい。
crを食わせると複数数式が出る
なぜか知らないが、crlfとかを間に入れてレンダリングすると同じ数式が何回も表示される。 バグっぽいが、一方で改行を入れなきゃいけない理由も無いのでスペースにして食わせてお茶を濁す。
だいたい対応終わる
かなり大変更だったが、1.5日くらいで終わった。 そんなにバグらなかったのと、思ったよりも集中して作業出来たから、というのの相乗効果か。
これまで気になってたバグもついでにいろいろ治ったし、マクロとかもちゃんとサポートされたし、大分本格的になってきたなぁ。 今回のspanのサイズ回りは結構根深いと思っていたので、割としっかり対応出来たのは良かった。 まだいくつか良く分かってない所はあるが、次何か分かりやすいissueが見つかった時には、ちゃんと直せる気がする。
線形計画法のノートを少し見てみたが、かなりいい感じに表示されているし、満足度高い。よしよし。