
Houdini で人工文字を再現する
第17回Houdiniゆるゆる会のテーマが「文字」だったため、人工文字「塔の字」を再現してみました。日本語を入力すると、塔の字で表現された文字が出力されます。

塔の字とは?
「塔の字」は人工文字の一種で、以下の特徴があります。
縦書きでシンメトリー
文節の区切り(文節ごとに分けられ、一文は特殊な役物で囲まれます)
ユニークなデザインと構造により、未知の言語のようなかっこよい仕上がりになります。
日本語を「塔の字」に変換する手順
日本語をローマ字へ変換
まず、日本語をローマ字に変換します。この変換では、Pythonを使用して辞書型のマッピングを作成し、効率化を図りました。また、スペースはアンダーバー(_)に置き換えてTextSymbolで扱いやすくしています。
ローマ字に変換するのはPythonであればライブラリも存在するのですが、今回はデータ配布に都合が良いので、単純に辞書型によるテーブルを作成して置き換えることにしました。
実際には、ChatGPTを活用して変換辞書を自動生成したので、作業の手間が大幅に省けました。
# ローマ字変換辞書
romaji_map = {
"あ": "a", "い": "i", "う": "u", "え": "e", "お": "o",
"か": "ka", "き": "ki", "く": "ku", "け": "ke", "こ": "ko",
"が": "ga", "ぎ": "gi", "ぐ": "gu", "げ": "ge", "ご": "go",
"さ": "sa", "し": "shi", "す": "su", "せ": "se", "そ": "so",
"ざ": "za", "じ": "zi", "ず": "zu", "ぜ": "ze", "ぞ": "zo",
"た": "ta", "ち": "chi", "つ": "tsu", "て": "te", "と": "to",
"だ": "da", "ぢ": "ji", "づ": "zu", "で": "de", "ど": "do",
"な": "na", "に": "ni", "ぬ": "nu", "ね": "ne", "の": "no",
"は": "ha", "ひ": "hi", "ふ": "fu", "へ": "he", "ほ": "ho",
"ば": "ba", "び": "bi", "ぶ": "bu", "べ": "be", "ぼ": "bo",
"ぱ": "pa", "ぴ": "pi", "ぷ": "pu", "ぺ": "pe", "ぽ": "po",
"ま": "ma", "み": "mi", "む": "mu", "め": "me", "も": "mo",
"や": "ya", "ゆ": "yu", "よ": "yo",
"ら": "ra", "り": "ri", "る": "ru", "れ": "re", "ろ": "ro",
"わ": "wa", "を": "wo", "ん": "n",
# 小文字のひらがな(促音・拗音など)
"ゃ": "ya", "ゅ": "yu", "ょ": "yo", "っ": "tsu",
" ": "_"
}
# 日本語テキストをローマ字に変換する関数
def to_romaji(text):
romaji_text = ""
for char in text:
if char in romaji_map:
romaji_text += romaji_map[char]
else:
romaji_text += char # 辞書にない場合はそのまま
return romaji_text
# 他のノードからテキストを取得
node = hou.pwd()
geo = node.geometry()
text = hou.pwd().inputs()[0].parm("text").eval()
romaji = to_romaji(text)
geo.addAttrib(hou.attribType.Global, "text", "")
geo.setGlobalAttribValue("text", romaji)
文を特殊な役物で囲む
「塔の字」では、一文を『。』で区切り、特殊な役物で囲む仕組みがあります。この処理のため、PythonSOPを使用しました。これもChatGPTに作成してもらいました。
node = hou.pwd() # 現在のノードを取得
geo = node.geometry() # ジオメトリを取得
# 他のノードからテキストを取得
text = geo.attribValue("text")
# 区切り文字
delimiter = "。"
# 1. テキストを区切り文字で分割
sentences = text.split(delimiter)
# 2. 各要素を「」で囲む(空文字を除外)
result = "".join([f"「{sentence.strip()}」" for sentence in sentences if sentence.strip()])
# アトリビュートを作成して結果を格納
geo.addAttrib(hou.attribType.Global, "result", "")
geo.setGlobalAttribValue("result", result)
こうしてできた変換された文字は以下のようになります。
オリジナル わたし は じんるい。ほろんじゃ った。ばい ばい。
編集後 「watashi_ha_zinrui」「horonziya_tsuta」「bai_bai」
変換結果の生成と配置
変換された文字列をHoudiniのFontSOPに入力することで、塔の字を視覚化できます。FontSOPのTextの中に、以下のように記載します。
FontSOPで表示することで変換された文字のtextsymbolを取得できます。
`details(-1, "result")`
この文字はTextIndexでPackにして、TextSymbolはアトリビュートとして残すようにしておきます。

さらに、文字間の調整を行うために以下のような処理を加えました。
AddSOPとCopyTransformを用いて文字数に応じたポイントを生成。
PackedPrimitiveからポイントにtextsymbolアトリビュートを移植。
ポイントへの移植には以下のVEXコードを使用しました。
int cnt = npoints(0);
for (int i = 1; i < cnt; i++) {
s@textsymbol = itoa(prim(1,"textsymbol", @ptnum));
}
非常に地味な見た目ですが、ここまでの結果です。


文字のアウトラインを定義
文字の見た目を作成します。ここは非常に地味な作業で、Curveを使って文字の半分だけをひたすら作りました。


作成したパーツをMergePackedします。MergePackedした際に、Nameアトリビュートが生成されますが、それを元にFontで文字を生成します。

Fontで生成することでtextsymbolが作成できました。
変換された文字を塔の字に置き換え
規則的に並んだ変換された文字と、塔の字のパーツのデータができたので、あとは、CopyToPointのPeiceAttributeでコピーします。

塔の字には文字の塊ごとを貫く中心線があるという独特のルールがあります。これを作成するために、スペースや役物を除去し、Connect Adjacent Piecesで近いポイントをポリラインでつなぎます。長く見えるラインもバラバラのポリラインなので、PolyPathで接続し、Facetで中点を消します。

上下に縮まるようにNormalを作成しておき、RaySOPで文章の塊にスナップさせます。

あとは簡単にSweepSOPやMirrorSOPでディテールをつけて完成です。
塔の字にはeで終わる文節は特殊な形状に変化するというルールもあるのですが、ここは割愛しました。
今回の作例では、PythonSOPとChatGPTが非常に便利でした。
