見出し画像

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));
}

非常に地味な見た目ですが、ここまでの結果です。

ポイントがtextsymbolアトリビュートを持っている

文字のアウトラインを定義

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

Sだとこんな感じ

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

Fontで生成することでtextsymbolが作成できました。

変換された文字を塔の字に置き換え

規則的に並んだ変換された文字と、塔の字のパーツのデータができたので、あとは、CopyToPointのPeiceAttributeでコピーします。

かなりそれらしい見た目になった

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

Connect Adjacent Pieces の存在を初めてしりました

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

ラインが綺麗に揃った

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

今回の作例では、PythonSOPとChatGPTが非常に便利でした。

いいなと思ったら応援しよう!