MagicaVoxelの.voxファイルを読む
https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt を参考にしてMagicaVoxelの.vox形式のファイルをスクリプトで読んだ。 少ないバイト数でreadを呼ぶのはシステムコールを多用していて遅そうだったり、PACKというチャンクの実装をスキップしていたり、改善の余地はまだまだあるがバイナリファイルにも手出し出来そうだと分かった。
from io import BufferedReader from typing import Tuple # reference: https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt VOX_HEADER = b"VOX " VOX_VERSION_NUMBER = 150 def main(): file = "./blue.vox" with open(file, "rb") as f: header = f.read(4) assert header == VOX_HEADER version: int = int.from_bytes(f.read(4), byteorder="little") assert version == VOX_VERSION_NUMBER while True: chunk_id = f.read(4) if len(chunk_id) == 0: exit() if chunk_id == b"MAIN": read_chunk_header(f) elif chunk_id == b"SIZE": bytes_of_chunk_content, _ = read_chunk_header(f) size_bytes = f.read(bytes_of_chunk_content) assert len(size_bytes) == 12 size_x = size_bytes[:4] size_y = size_bytes[4:8] size_z = size_bytes[8:12] print(size_x, size_y, size_z) elif chunk_id == b"XYZI": bytes_of_chunk_content, _ = read_chunk_header(f) num_voxels = int.from_bytes(f.read(4), byteorder="little") assert bytes_of_chunk_content == num_voxels * 4 + 4 for _ in range(num_voxels): x, y, z, i = f.read(4) print(x, y, z, i) elif chunk_id == b"RGBA": read_chunk_header(f) for _ in range(256): r, g, b, a = f.read(4) print(r, g, b, a) elif chunk_id == b"PACK": raise NotImplementedError() else: print(f"unknown Chunk {chunk_id}") bytes_of_chunk_content, bytes_of_children_chunks = read_chunk_header(f) f.read(bytes_of_chunk_content) f.read(bytes_of_children_chunks) def read_chunk_header(f: BufferedReader) -> Tuple[int, int]: bytes_of_chunk_content = int.from_bytes(f.read(4), byteorder="little") bytes_of_children_chunks = int.from_bytes(f.read(4), byteorder="little") return (bytes_of_chunk_content, bytes_of_children_chunks) if __name__ == "__main__": main()
手元の自作pythonライブラリを競プロでも活用したいのでツールを作った
概要
競プロなど単一のファイル提出が求められるケースに対応するために、手元の複数ファイルにまたがるpythonコードを組み合わせて単一の提出ファイルを作成するコマンドラインツールsinglue
を作成しています。
2022年9月21日時点のバージョン(v0.1.5
)での実行方法について紹介します。
背景
手元のディレクトリ内で定義されている関数やクラスをimportしながら作成したプログラムをオンラインジャッジの環境に提出すると手元でしか定義されていないので当然エラーが出ます。 そのため提出するファイルに、コピペしたり、あらかじめ使うであろうものを記述しておいたり、スニペットとして展開できるようにしたりといった方法があるかと思います。 いずれにおいても手動のオペレーションでミスする、量の多いテンプレートの見た目に圧倒される、スニペットのメンテナンスをミスする(改良した部分がスニペットには反映されていない)、といった経験があるのではないでしょうか(私は3つともありました)。
そこで1ファイルが巨大にならないようにライブラリとして整備しつつ、コピペミスによるエラー、実行環境の差異によるエラーが起こらないようにsinglue
というコマンドラインツールを作成しました。
イメージ
概念を図で表すと以下のようなイメージです。
手元のファイル群(青枠で囲った部分)から提出用ファイル(赤枠部分、output.py
)を生成します。
使用方法
インストール方法、実行例について説明します。
インストール方法
astモジュールにpython3.9以降に追加されたunparseに依存しているためpython3.9以降の環境が必要になります。
この環境が既にあればpip install singlue
で事足りますが、無い場合は以下の手順でインストール出来るかと思います。
実行例
前提
以下のようなディレクトリ構造を仮定します。
$ tree . ├── library.py └── main.py
また各ファイルの内容は以下のものとします。
main.py
from library import one, two, Three assert one() + two() == Three().three()
library.py
def one() -> int: return 1 def two() -> int: return 2 class Three: def __init__(self): self.value = 3 def three(self): return self.value
実行コマンド
singlue main.py > output.py
上のコマンドを実行すると以下の内容のoutput.py
というファイルが作成されます。
ジャッジ環境において解決できずエラーを起こすimport文が削除され、代わりにimportする対象のクラスや関数が埋め込まれているのが確認出来ます。
なお、説明の都合上ファイル名をmain.py
、library.py
としていますがこの命名である必要はないです。
またライブラリとしているファイルはmain.py
と同一のディレクトリ階層にあるという制約はありますがlibrary.py
に類するファイルが複数であっても動きます。
output.py
def one() -> int: return 1 def two() -> int: return 2 class Three: def __init__(self): self.value = 3 def three(self): return self.value assert one() + two() == Three().three()
課題
混み入った依存関係が必要になるケースは少ないのではないかと感じていますが、多段の依存関係の解決(ファイル内、ファイル間)が出来ないのが現在の課題です。
最後に
コンテストにぶっつけ本番で使用することはおすすめしません。日々の精進等で試していただけると嬉しいです。
課題等を報告する場合はGitHubのIssues(https://github.com/kawagh/singlue/issues)にてお願いします。
pipxでpython3.9依存のツールを利用できるようにした
背景
python3.8
がシステムのデフォルトの環境だがpython3.9
に依存する自作CLIツール(https://github.com/kawagh/singlue)をグローバルに扱いたかった。
前提の環境
- OS
$ uname -sv Linux #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022
- pipx(https://github.com/pypa/pipx)がインストールされている
方法
python3.9
をapt
でインストール- pipxで依存するpythonのバージョンを指定してツールをインストール
sudo apt install python3.9 sudo apt install python3.9-venv pipx install singlue --python python3.9
その他
ツールの開発時はpyenv
でpython3.9
を指定していたのでそれをpipx
の方で参照しても良かったのかもしれない。今ではapt
とpyenv
それぞれでインストールしたpython3.9
が存在しているが、手軽さを優先した。
テストの存在するディレクトリを指定してpytestの実行を速める
多くのファイルを含むディレクトリが存在するとpytestがその中も探していてテストの完了が遅くなっていた。
poetryでプロジェクトを管理しているのでpyproject.toml
に以下のようにテストの存在するパスを明示すると遅くなっていたのが解決した。
[tool.pytest.ini_options] testpaths= ["tests"]
他のconfigファイルの場合の記載例もドキュメントに記述されている。
参考
vimの選択範囲のテキストを外部コマンドに渡す
選択範囲のテキストをレジスタを経由してvimscriptの変数とし、その変数の出力を一時ファイルにリダイレクトして外部コマンドを実行することで実現できる。
その一例として、VisualモードでDOT言語のコードを選択した状態で以下の関数を呼び出して表現されるグラフの画像を取得できる。
function! MakeDotGraphFromSelected() let tmp = @@ silent normal gvy redir! > tmp.dot let selected = @@ silent echo selected redir END call system('dot -Tpng -o tmp.png tmp.dot') call delete('tmp.dot') let @@ = tmp endfunction
参考
シェルの補完で大文字小文字の区別無く効くようにする
.bashrcに以下の行を追加する。
cat r<Tab>
で cat README.md
といった具合に補完されるようになる。
bind 'set completion-ignore-case on'
Androidアプリ内チュートリアル作成の取り組み
新規でアプリをインストールした際に下のように画面を表示できるようにしたいと思っています。
実装手順
- LaunchedEffect()とdelay()でmutableState(チュートリアルを画像の集まりと考えてsceneと呼んでいる)を一定時間おきに進める
- そのmutableStateを提供するcompostionLocalを作成する
- ハイライトやテキストを付加させたいcomposableのmodifierをcompositionLocalの値に応じて渡す(以下にそのコード片を示す)
val textPaint = Paint().asFrameworkPaint().apply { textSize = 50.sp.value color = android.graphics.Color.BLACK } val modifierForButton:Modifier = if (isTutorial && LocalScene.current == 0) Modifier.drawBehind { drawCircle( Color.LightGray, this.size.maxDimension, alpha = 0.5f ) drawIntoCanvas { canvas: Canvas -> canvas.nativeCanvas.drawText( "description1", this.center.x, this.center.y + 90, textPaint ) } } else Modifier
改善したい点
- 条件分岐に応じてmodfierForButtonなどといった定義を要すること。
当初はModifierの拡張関数としてModifier.explainedBy(scene:Int):Modifier
などと定義して即座にcomposableに渡せたらと思っていたが、そのようにしてcompositionLocalの値を読もうとすると@Composable invocations can only happen from the context of a @Composable function
と言われてしまい出来なかった。