2025年7月24日木曜日

土坑部分の概略3Dモデリング

 Rough 3D modeling of the pit section


I did a rough 3D modeling of the small overhanging part of the Jomon pit where four pits cut each other. By doing the rough 3D modeling, I was able to correct my misunderstanding.


4つの土坑が切りあっている縄文土坑のオーバーハング小部分について、概略3Dモデリングをしました。概略3Dモデリングをすることにより、自分の勘違いを訂正できました。

1 有吉北貝塚土坑SK657A/B/C/D部分の概略3Dモデリング


吉北貝塚土坑SK657A/B/C/D実測図の検討図をBlenderにプロットした画面


有吉北貝塚土坑SK657A/B/C/D実測図の検討図をBlenderにプロットした画面(部分3Dモデリング結果表示)


3Dモデリングのために描画したべジェ曲線


作成した3Dモデル


作成した3Dモデル


作成した3Dモデル


作成した3Dモデル

2 メモ

当初作成した3Dモデルは間違っていました。


当初作成した間違った3Dモデルと訂正して3Dモデル

この土坑は当初土坑2つを新たな土坑が切っている部分にオーバーハング部分があるのですが、最初作成した3Dモデルは当初土坑裾線の定高性が保たれていませんでした。この初歩的なミス・勘違いは、自分は、3Dモデルを実際に作成してはじめて気が付くことができました。


2025年7月23日水曜日

縄文遺構の3Dモデリング基礎取組み日誌(2025.07.23)

 Basic Initiative Diary for 3D Modeling of Jomon Remains (2025.07.23)


I am working on mastering the basic techniques for 3D modeling of Jomon remains. I attempted to do a rough 3D modeling of a part of a case that was difficult for me.


縄文遺構の3Dモデリング基礎技術の習熟に取組んでいます。自分にとって難易度の高い事例の部分について概略3Dモデリングを試みました。

1 有吉北貝塚土坑SK657A/B/C/D部分の概略3Dモデリング


有吉北貝塚土坑SK657A/B/C/D実測図の検討図をBlenderにプロットした画面


有吉北貝塚土坑SK657A/B/C/D実測図の検討図をBlenderにプロットした画面(部分3Dモデリング結果表示)


3Dモデリングのために描画したべジェ曲線

Blenderで、3Dモデリングのために描画したべジェ曲線は全部で6本です。このべジェ曲線をコピー(Shift+D)して、高さを変化させるなどして使いまわして、3Dモデリングを作成しました。


作成した3Dモデル


作成した3Dモデル


作成した3Dモデル


作成した3Dモデル

2 感想

2-1 空間識の虚弱さは場数を踏むことで挽回するしかない

実測図から空間の状況を思い浮かべ、べジェ曲線をどのように描いたら、思い浮かべた3Dモデリングができるかという認識力を空間識と呼ぶことにします。その空間識が、自分はとても虚弱であることを思い知らされました。空間の様子を漠然と思い浮かべることはできます。しかし、それをべジェ曲線の3D配置で的確に表現できないのです。平面図(地図など)は得意だけれども、立面図は分析対象にしてこなかったことの跛行性を思い知らされます。嘆いていてもらちが空きませんから、3Dモデリングの場数を踏むことにします。縄文遺構には複雑なものがあるといっても形状そのものは「たかが知れている」ことは明白です。従って、3Dモデリングの場数を踏めば、なんとかなると楽観します。

2-2 3Dモデリングのステップ

実測図として表現された縄文遺構に複雑な3D形状がある場合、その部分だけ概略3Dモデリングして、3D形状の確認を行うことは、空間識が虚弱な自分の現状では、大切であると考えます。

概略3Dモデリング→本3Dモデリングとステップを踏み、空間識を高めることが大切であると考えます。

2-3 3Dモデリングのより高度な取組み展望

現在の自分の取組みは、発掘調査報告書実測図に表現された3D形状を、そのままいかに効率的に3Dモデリングするかということです。それは大切で基本的なことです。

しかし、縄文遺構の3D形状はそれぞれ縄文人が所定の機能を得るために造形したものです。従って3Dモデリングは、より本質的には、実測図そのものを表現するのではなく、縄文人が利用した機能を的確に表現する3Dモデル復元でなければなりません。つまり、遺構の利用・機能について深い知識が、3Dモデリング実務者に求められます。

また、実測図と発掘写真を照合すると、実測図を金科玉条のもとして扱うことは適切でない場合があることにも気が付きます。


2025年7月20日日曜日

ドイツ考古学切手 国家的考古学的出土品

 German Archaeological Stamps - National Archaeological Finds


I enjoyed obtaining the archaeological stamps issued in Germany as part of the Europe 2025-Archeological Discoveries series. They feature designs of Paleolithic finds from 40,000 years ago, such as Venus statues, lion man, and bird-bone flutes, and are endlessly fascinating.


ヨーロッパ2025-考古学的発見シリーズとしてドイツで発行された考古学切手を入手して楽しみました。ヴィーナス像、ライオンマン、鳥骨製笛など4万年前旧石器時代出土品がデザインされていて、興味が尽きません。

1 ドイツの考古学切手 国家的考古学的出土品


ドイツの考古学切手 国家的考古学的出土品

ヨーロッパ2025-考古学的発見シリーズ

ユネスコ世界遺産「Höhlen und Eiszeitkunst der Schwäbischen Alb」(シュヴェービッシェ・アルプの洞窟と氷河期芸術)


ヴィーナス像(マンモス牙製)


ヴィーナス像写真(https://www.weltkultursprung.de/から引用)


ライオンマン(マンモス牙製)


ライオンマン写真(https://www.weltkultursprung.de/から引用)


鳥骨製笛(シロエリハゲワシ骨製)


鳥骨製笛写真(https://www.weltkultursprung.de/から引用)

2 感想

これが「考古学切手だ!」と声を出したくなるような切手です。デザインされた遺物はどれもこれも興味が尽きないものばかりです。

いずれも洞窟から出土した製品で、約3万5千年前から約4万年前の旧石器時代遺物です。

ヴィーナス像は乳房と陰部が強調され、頭部は極端な省略形です。

ライオンマンは、ライオンを祖先として崇めたトーテム信仰の具象形でしょうか。

鳥骨製笛は人類最古の楽器といわれています。



2025年7月19日土曜日

技術メモ べジェ曲線を使った単純モデリング

 Technical memo: Simple modeling using Bezier curves


I made a note of the steps to model simple shapes such as pits. Draw the border line with a Bezier curve, and reuse it to create individual unit terrain surfaces using the add-on Curves To Mesh function.


土坑等の単純形をモデリングする際の手順をメモしました。縁取線をべジェ曲線で描き、それを使いまわしながら、アドオンCurves To Mesh機能により単位地形面を個別に造形します。

1 単位地形面を2本のべジェ曲線で造形する

全ての単位地形面を2本のべジェ曲線の間を面張りすることによって表現することにします。面張りの実務はアドオンCurves To Meshにより行うものとします。

べジェ曲線の平面座標は資料平面図から、高さ座標は資料断面図から補間により獲得します。

2025.07.11記事「縄文遺構実測図の縁取線(地形変換線)の高度線形補間Blenderアドオン

2 縁取線をべジェ曲線で描く

全ての単位地形面の縁取線をべジェ曲線で描きます。


べジェ曲線による縁取線描画結果

作業の順番は、斜面造形を優先して行うことにします。斜面造形がしやすいようにべジェ曲線描画を工夫します。


べジェ曲線の描画単位

土坑を巡る斜面はべジェ曲線abに、段差斜面はべジェ曲線cdに対応します。

3 単位地形面の造形


造形結果


造形結果(単位地形面)

アドオンCurves To Meshにより、斜面1はべジェ曲線abから、斜面2はべジェ曲線cdから直接造形できます。

平坦面3はべジェ曲線bの一部とべジェ曲線cから造形します。

なお、べジェ曲線を使いまわす際、そのコピーはShift+Dで行います。(コピー元とコピー先にリンク関係が生じないようにするため。)

べジェ曲線の分離は編集モードで分離したい部分を指定してからPで行います。

平坦面4の造形はべジェ曲線d、bの一部(2箇所)、eを2本のべジェ曲線に整理してから、造形します。

ここではべジェ曲線bの一部(2箇所)とeを1本に統合しました。


造形結果

4 感想

斜面と平坦面という単位地形面毎に一つ一つ造形する手間をかけないと、結局望むレベルのモデリングができないことを認識しました。

なお、土坑の中の穴については、Blender合成により対応できます。

2025.07.15記事「土坑SK773A/B(千葉市有吉北貝塚)3Dモデル



2025年7月15日火曜日

土坑SK773A/B(千葉市有吉北貝塚)3Dモデル

 Pit SK773A/B (Ariyoshikita Shell Mound, Chiba City) 3D Model


I created a 3D model of two pits that are cut one after the other. As a beginner in Blender 3D modeling, this was a slightly more difficult task for me. When I observed the completed 3D model, I was reminded of the possibility of using A/B simultaneously, which was not possible from the planar and cross-sectional data.


切った切られたの関係にある2つの土坑の3Dモデルを作成しました。Blender3Dモデリング初心者の自分にとって少し難易度が上がった作業となりました。完成3Dモデルを観察すると、平面・断面資料からは生まれなかった、A/B同時利用の可能性を想起しました。

1 土坑SK773A/B(千葉市有吉北貝塚)3Dモデル

土坑SK773A/B(千葉市有吉北貝塚)3Dモデル


発掘調査報告書資料

3DF Zephyr v8.013でアップロード


3Dモデルの動画


3Dモデルの画像


3Dモデルの画像

2 モデリング技術メモ

2-1 モデリングの順番

土坑周辺の地面(a)、切った土坑概形(b)、切られた土坑概形(c)、土坑に掘られた穴5箇所(d)のパーツに分け次の順序でモデリング作業を行いました。

b→c→bとcの統合→d(5つ)→bとcの統合モデルとdの合成(5回)→a→bcd合成モデルとaの統合

2-2 ブーリアン合成(ユニオン)

段階的に下に凸形状で構成される土坑、陥穴、竪穴住居、炉穴などの縄文遺構ではブーリアン合成(ユニオン)が有用性あるツールになります。


ブーリアン合成イメージ


参考 資料の分析

3 メモ

「土器1~3はAに、土器4~6はBに帰属する。土器は両土坑ともに磨消文成立前後の加曽利EⅡ式がまとまっている。両土坑とも9群~10群土器の範囲でとらえる。ただし、遺物の出土状況はAよりBの方が新しい様相を呈している。」(発掘調査報告書から引用)

「遺物の出土状況はA(切った土坑)よりB(切られた土坑)の方が新しい様相を呈している。」との記述が具体的に何を指しているのか、断面図を見ても判りません。もしかしたら、覆土層の中に上下関係が観察され、B覆土層がA覆土層の上に乗っているように観察されたのかもしれません。

発掘調査報告書のこの記述を信じると、自分が抱いていた次のイメージが崩れます。

●資料から自分が抱くイメージ。

・土坑Bが掘られ、使われ、廃用となり、覆土で覆われた。

・覆土で覆われた土坑Bの近くで土坑Aが掘られ、土坑Aが利用された。土坑Aの壁の一部は土坑Bの覆土層である。

・土坑Aが廃用となり、覆土で覆われた。

発掘調査報告書記述から、次のような状況も検討する必要があると考えます。

●土坑A/Bの利用イメージ例

・土坑Bが掘られ、使われた。

・ある時期に土坑Aが掘られ、空間的に土坑Bと連結して、土坑A・B空間が同時に使われた。

・土坑A/Bが廃用になり、深い土坑A部分が先に覆土で覆われ、その後、浅い土坑Bが覆土で覆われた。この様子が覆土層に表現され、発掘調査者は、A覆土層の上にB覆土層が乗っているように観察した。


2025年7月13日日曜日

有吉北貝塚北斜面貝層の遺物分布図座標入力作業日誌(2025.07.13)

  Work log for inputting coordinates of artifacts on the distribution map of the shell layer on the north slope of the Ariyoshi Kita shell mound (2025.07.13)


We are currently working on obtaining the XY coordinates of the artifacts from the artifact distribution map of the shell layer on the north slope of the Ariyoshi Kita Shell Mound using a homemade Python tool, and combining them with the Z coordinates already obtained from the artifact ledger to obtain the artifact's 3D coordinates. The artifact distribution appears to be stratified, and it is of interest to consider its relationship with the development of the shell layer. So far, the 3D distribution of 13,914 artifacts has been clarified.


有吉北貝塚北斜面貝層の遺物分布図から、遺物のXY座標を自家製Pythonツールで取得し、既に遺物台帳から取得しているZ座標と合わせて遺物3D座標を取得する活動を進めています。遺物分布は成層的に見えて、貝層発達との関わりに興味が深まります。これまでに13914遺物の3D分布が明らかとなりました。

1 有吉北貝塚北斜面貝層の遺物分布図データ(2025.07.13現在)

有吉北貝塚北斜面貝層の遺物分布図データ(2025.07.13現在)

13914遺物データ

3DF Zephyr v8.013でアップロード


3Dモデルの動画


3Dモデルの画像


3Dモデルの画像

2 メモ

北斜面貝層が収まるガリー侵食地形の谷頭部付近の遺物3D分布が明らかとなってきました。成層構造が見えます。貝層成層と関連していると考えられ、貝層発達と貝層利用の関係分析が楽しみになります。年内に遺物平面座標読取作業を終えることを目標にして、それに集中して活動することにします。分析活動はその後に思う存分行うことにし、今は我慢しておきます。

平面座標読取活動の効率化が進んでいます。一度作成して効率化に使っているPythonスクリプトについて、さらなる効率化のための改善を加えることがたびたびあります。最初段階では、手作業ではなく、自動化できれば素晴らしいという思考でしたが、現在では同じ自動化でもより手数が少なく、単純なものに改善してます。

今回作業分の範囲に遺物出土がない50㎝×50㎝平面が3箇所あります。発掘調査報告書資料で確認すると、サンプル抽出空間です。この平面では貝層サンプルをボーリング柱状資料のように貝層最上部から最下部まで連続して採っています。


サンプル抽出空間

3 技術メモ

既に約1万データがプロットされているBlenderファイルに、今回約4000データを追加プロットしました。その4000データプロットになんと1時間5分かかりました。時間がかかりすぎます。プロットでは遺物コードをアウトライナーに書かせています。おそらく、Blenderでは、4000データプロットに際して、その遺物コードがすでにプロットしてある1万データの遺物コードと同じものがあるかどうかチェックしています。(例 同じものがあると2250023、2250023(1)のように重複を避けます。)この遺物コードチェックにほとんどの時間をかけていると推察します。遺物コードなしで、単純にCUBEをプロットするだけなら、10分もかからない作業であると推察します。今後、4000データを更のBlenderファイルにプロットし(恐らく20分程度)、それを1万データファイルにアペンドするなどの方法で時短になるか、検討することにします。

2025年7月11日金曜日

土坑SK082(千葉市有吉北貝塚)3Dモデル

 Pit SK082 (Ariyoshikita Shell Mound, Chiba City) 3D Model


I created a 3D model of a pit with two holes dug at the bottom that overhang the wall. It seems worthwhile to investigate why the holes overhang the wall.


底面に、壁面に対してオーバーハングしている穴が2つ掘られた土坑の3Dモデルを作成しました。穴が壁面に対してオーバーハングしている理由の検討は価値があるように感じます。

1 土坑SK082(千葉市有吉北貝塚)3Dモデル

土坑SK082(千葉市有吉北貝塚)3Dモデル


発掘調査報告書資料

3DF Zephyr v8.013でアップロード


3Dモデルの動画


3Dモデルの画像


3Dモデルの画像

2 メモ

自作Blenderアドオン「Bezier Z Interpolator」を使うことにより、精度の高い3Dモデリングが可能となり、3Dモデリング作業に対する納得感が少しずつ出てきました。

3Dモデルは底面の2つの穴のうち1つを除いたモデルを作成し、除いた穴のモデルを別につくり、ブーリアン(アドオンbool tool利用)で合成しました。


参考 資料の分析

土坑底面に、壁面に対してオーバーハングしている穴が2つ掘られています。2つの穴が底面に対してオーバーハングしている理由の検討は価値があるように直観します。方位に関連して風除けとか日射量などに関連するのでしょうか?そもそもこの土坑の利用の仕方自体が自分には全くわかっていません。当面は、考古学的考察ではなく、3Dモデリング技術習得に焦点を当てた活動を展開することにします。

縄文遺構実測図の縁取線(地形変換線)の高度線形補間Blenderアドオン

 Blender add-on for linear interpolation of the border lines (terrain transformation lines) of Jomon ruins survey maps


The border lines (terrain transformation lines) of Jomon ruins survey maps are not contour lines, but change in altitude. I created a Blender add-on that linearly interpolates the altitude distribution of the entire border line from several known point altitudes and displays it in the Blender3D viewport. An essential basic tool for 3D modeling of Jomon ruins has been completed.


縄文遺構実測図の縁取線(地形変換線)は等高線ではなく、高度が変化します。判明している幾つかのポイント高度から縁取線全体の高度分布を線形補間して、Blender3Dビューポートに表示するBlenderアドオンを作成しました。縄文遺構3Dモデリングの必須基礎ツールが完成しました。

1 縁取線高度線形補間Blenderアドオンの機能

このアドオンでは、3Dビューポートの真上からのビュー(テンキー7)で描いた縁取線(べジェ曲線)の頂点インデックスに2つ以上の既知の高度数値を手入力します。アドオンは、入力された既知高度数値に基づき、それ以外の頂点インデックスに線形補間(内挿、外挿)で高度数値を生成し、縁取線頂点高度が3Dビューポートで移動します。

2 縁取線高度線形補間Blenderアドオンの使い方

2-1 縁取線の作成・表示

Blender3Dビューポートに縄文遺構実測図を真上からのビュー(テンキー7)でスケール通りに配置します。この実測図を下敷きにして目的の縁取線をBezier Toolkitを使ってクリックによりべジェ曲線で生成します。

生成したべジェ曲線を編集モードで選択しておきます。

なお、閉じたべジェ曲線の場合は「オブジェクトデータプロパティ→アクティブスプライン→ループのUチェックを外す」操作で開いたべジェ曲線にしておきます。

2-2 アドオンでの高度線形補間

2-2-1 3Dビューポートにおけるインデックス(番号)の表示

アドオンの「インデックス表示切替」をクリックして、3Dビューポートの縁取線(べジェ曲線)頂点にインデックス(番号)を表示します。

2-2-2 初期化

「初期化」をクリックして、ID欄(頂点番号欄)とZ値欄(標高欄)をアドオン画面に表示します。

ID欄は全て表示されていますが、Z値欄は空欄となっています。

2-2-3 既知高度の手入力

実測図から判明している既知高度を関連する頂点のZ値欄に手入力します。補間のために、2つ以上の値を入力する必要があります。


既知高度を入力した様子(54頂点に対して既知高度を4頂点に手入力)


既知高度を入力した様子(高度は全て0となっている)

2-2-4 補間とその反映

「補間して反映」をクリックすると、Z値欄の空欄部分に線形補間された数値(高度)が生成されるとともに、3Dビューポートでべジェ曲線頂点が補間高度に移動します。


線形補間した様子(X座標、Y座標は不動)


線形補間した様子(頂点のZ座標が補間高度に移動している)


参考 土坑実測図(土坑肩線の高度が読みとれるのは4点)

2-2-5 やり直し

不都合があれば、「初期化」をクリックして、Z値欄に既知高度を入力し直し、「補間して反映」をクリックします。

2-2-6 確定

不都合がなければ、「確定」で結果を確定します。

元が閉じたべジェ曲線であった場合は、「オブジェクトデータプロパティ→アクティブスプライン→ループのUをチェックする」で開いたべジェ曲線から閉じたべジェ曲線に戻します。

3 メモ

高度線形補間を行った縁取線を使うことによって、縄文遺構3Dモデリングの精度を上げることができます。

このアドオンはBlenderバージョン4.4.3を対象にしています。

4 感想

このアドオンは「べジェ曲線の頂点インデックス(番号)表示」BlenderPythonスクリプト及び、「内挿と外挿を含む線形補間」Pythonスクリプトを最初に作成し、その2つを主な要素として新たに構成したPythonスクリプトです。それぞれの段階でのChatGPTとのやりとりはいつも以上に回数が多くなりました。恐らく、このアドオンの機能を最初からChatGPTに求めても、完成に至らなかったと想像します。

5 Pythonスクリプト


bl_info = {
    "name": "Bezier Z Interpolator",
    "blender": (4, 4, 3),
    "category": "Curve",
}

import bpy
import blf
from bpy_extras import view3d_utils
import numpy as np

handler = None

def draw_callback_px(self, context):
    obj = context.active_object
    if not obj or obj.type != 'CURVE' or obj.mode != 'EDIT':
        return
    region = context.region
    rv3d = context.space_data.region_3d
    curve = obj.data

    font_id = 0
    blf.size(font_id, 14)
    for spline in curve.splines:
        if spline.type != 'BEZIER':
            continue
        for i, bez in enumerate(spline.bezier_points):
            co_world = obj.matrix_world @ bez.co
            screen_coord = view3d_utils.location_3d_to_region_2d(region, rv3d, co_world)
            if screen_coord:
                blf.position(font_id, screen_coord.x, screen_coord.y, 0)
                blf.draw(font_id, str(i))

def register_draw_handler():
    global handler
    if handler is None:
        handler = bpy.types.SpaceView3D.draw_handler_add(
            draw_callback_px, (None, bpy.context), 'WINDOW', 'POST_PIXEL'
        )

def unregister_draw_handler():
    global handler
    if handler is not None:
        bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
        handler = None

def interpolate_z(values):
    ids = np.array([i for i, v in enumerate(values) if v != ""])
    zs = np.array([float(v) for v in values if v != ""])
    if len(ids) < 2:
        return values
    f = np.interp(range(len(values)), ids, zs)
    return [str(round(z, 4)) for z in f]

class CurveZEntry(bpy.types.PropertyGroup):
    z: bpy.props.StringProperty(name="Z", default="")

class CURVE_PT_z_interpolator_panel(bpy.types.Panel):
    bl_label = "Z値補間ツール"
    bl_idname = "CURVE_PT_z_interpolator_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Bezier Z"

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        layout.operator("curve.toggle_index_display", text="インデックス表示切替")
        layout.operator("curve.z_initialize", text="初期化")

        layout.label(text="Z値入力:")
        for i, entry in enumerate(scene.bezier_z_entries):
            row = layout.row()
            row.label(text=f"ID {i}")
            row.prop(entry, "z", text="")

        layout.operator("curve.z_interpolate", text="補間して反映")
        layout.operator("curve.z_confirm", text="確定")

class CURVE_OT_z_initialize(bpy.types.Operator):
    bl_idname = "curve.z_initialize"
    bl_label = "Zエントリ初期化"

    def execute(self, context):
        obj = context.active_object
        if not obj or obj.type != 'CURVE':
            self.report({'WARNING'}, "カーブオブジェクトを選択してください")
            return {'CANCELLED'}
        context.scene.bezier_z_entries.clear()
        for spline in obj.data.splines:
            if spline.type == 'BEZIER':
                for _ in spline.bezier_points:
                    context.scene.bezier_z_entries.add()
        self.report({'INFO'}, "Zエントリを初期化しました")
        return {'FINISHED'}

class CURVE_OT_z_interpolate(bpy.types.Operator):
    bl_idname = "curve.z_interpolate"
    bl_label = "Z値補間"

    def execute(self, context):
        entries = context.scene.bezier_z_entries
        z_list = [e.z for e in entries]
        z_interp = interpolate_z(z_list)

        obj = context.active_object
        if obj and obj.type == 'CURVE':
            index = 0
            mw = obj.matrix_world
            imw = mw.inverted()

            for spline in obj.data.splines:
                if spline.type != 'BEZIER':
                    continue

                bez_points = spline.bezier_points
                n = len(bez_points)

                for i, bez in enumerate(bez_points):
                    if index >= len(z_interp):
                        break

                    # グローバル座標でZ補正
                    world_co = mw @ bez.co
                    world_co.z = float(z_interp[index])
                    bez.co = imw @ world_co
                    entries[index].z = z_interp[index]
                    index += 1

                # 始点・終点のハンドルをたたむ
                if n >= 2:
                    p_start = spline.bezier_points[0]
                    p_end = spline.bezier_points[-1]

                    p_start.handle_left = p_start.co.copy()
                    p_start.handle_right = p_start.co.copy()
                    p_end.handle_left = p_end.co.copy()
                    p_end.handle_right = p_end.co.copy()

        return {'FINISHED'}

class CURVE_OT_z_confirm(bpy.types.Operator):
    bl_idname = "curve.z_confirm"
    bl_label = "Z値確定"

    def execute(self, context):
        self.report({'INFO'}, "Z値を確定しました")
        return {'FINISHED'}

class CURVE_OT_toggle_index_display(bpy.types.Operator):
    bl_idname = "curve.toggle_index_display"
    bl_label = "インデックス表示切替"

    def execute(self, context):
        if handler is None:
            register_draw_handler()
            self.report({'INFO'}, "インデックス表示を開始")
        else:
            unregister_draw_handler()
            self.report({'INFO'}, "インデックス表示を停止")
        return {'FINISHED'}

classes = (
    CurveZEntry,
    CURVE_PT_z_interpolator_panel,
    CURVE_OT_z_initialize,
    CURVE_OT_z_interpolate,
    CURVE_OT_z_confirm,
    CURVE_OT_toggle_index_display,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.bezier_z_entries = bpy.props.CollectionProperty(type=CurveZEntry)
    # register() では active_object にアクセスしない!

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    unregister_draw_handler()
    del bpy.types.Scene.bezier_z_entries

if __name__ == "__main__":
    register()

2025年7月9日水曜日

Blenderべジェ曲線の頂点インデックス(番号)表示

 Blender Bezier curve vertex index (number) display


Blender Bezier curve vertex index (number) display is now possible. This allows efficient and accurate manipulation of Bezier curves. This BlenderPython has been completed for the first time in a month.


Blenderべジェ曲線の頂点インデックス(番号)表示ができるようになりました。これでべジェ曲線の操作が効率的かつ正確にできるようになります。1カ月ぶりにこのBlenderPythonが完成しました。

1 Blenderべジェ曲線の頂点インデックス(番号)表示


Blenderべジェ曲線の頂点インデックス(番号)表示

この例は閉じたべジェ曲線で、0~77のインデックスが表示されています。

2 Blenderバージョン4.4.3用の頂点インデックス表示BlenderPythonスクリプト


import bpy
import blf
from bpy_extras import view3d_utils

handler = None

def draw_callback_px(self, context):
    obj = context.active_object
    if not obj or obj.type != 'CURVE' or obj.mode != 'EDIT':
        return

    region = context.region
    rv3d = context.space_data.region_3d
    curve = obj.data

    font_id = 0
    blf.size(font_id, 14)

    for spline in curve.splines:
        if spline.type != 'BEZIER':
            continue

        for i, bez in enumerate(spline.bezier_points):
            if not bez.select_control_point:
                continue

            # 3D位置 → スクリーン座標
            co_world = obj.matrix_world @ bez.co
            screen_coord = view3d_utils.location_3d_to_region_2d(region, rv3d, co_world)

            if screen_coord:
                blf.position(font_id, screen_coord.x, screen_coord.y, 0)
                blf.draw(font_id, str(i))

def register_draw_handler():
    global handler
    if handler is None:
        handler = bpy.types.SpaceView3D.draw_handler_add(
            draw_callback_px, (None, bpy.context), 'WINDOW', 'POST_PIXEL'
        )
        print("インデックス表示を開始しました。")

def unregister_draw_handler():
    global handler
    if handler is not None:
        bpy.types.SpaceView3D.draw_handler_remove(handler, 'WINDOW')
        handler = None
        print("インデックス表示を停止しました。")

class CURVE_OT_toggle_index_display(bpy.types.Operator):
    bl_idname = "curve.toggle_index_display"
    bl_label = "ベジェインデックス表示切り替え"

    def execute(self, context):
        if handler is None:
            register_draw_handler()
            self.report({'INFO'}, "表示開始")
        else:
            unregister_draw_handler()
            self.report({'INFO'}, "表示停止")
        return {'FINISHED'}

def register():
    bpy.utils.register_class(CURVE_OT_toggle_index_display)

def unregister():
    bpy.utils.unregister_class(CURVE_OT_toggle_index_display)

if __name__ == "__main__":
    register()
    bpy.ops.curve.toggle_index_display()

3 頂点インデックス(番号)表示の意義

Blenderで、XY座標平面に縄文遺構の幾つかの縁取線をトレースし、それらの縁取線を該当する標高に移動し、その縁取線からアドオンCurves To Meshで3Dモデルを作成します。しかし、縁取線は等高線ではなく、標高が変化する線分です。その標高変化を表現するには、標高が判明している少数の頂点の間をトポロジー的に補間する必要があります。その補間作業のために、標高判明頂点を把握する必要があり、どうしても、全体の頂点インデックスが必要になります。

このBlenderPythonスクリプトにより、その頂点インデックスが3Dビューポートで一見して判るようになりました。

縄文遺構3Dモデル構築作業にとって重要な基礎ツールの一つができました。

4 メモ

このBlenderPythonスクリプトは1カ月程前にChatGPTにより作成チャレンジして、半分失敗しました。当時作成したスクリプトはBlenderバージョン3では使えましたが、バージョン4では使えませんでした。失敗した理由は、ChatGPTにBlenderバージョンを明示しなかったからであると推察します。

今回はBlenderバージョン4.4.3で使うことを前提に指示しました。ChatGPTとのやりとりの中で、Blenderバージョン4.4の特性に関する扱いが幾つか出てきました。このような経緯から、Blenderのバージョンを明示しなければ、今回も失敗していた可能性があります。


べジェ曲線カラー化でやる気が出る

 Coloring Bezier curves motivates me


I'm working hard to learn the basics of Blender in order to proceed with the project to create a 3D model of Jomon ruins. While coloring Bezier curves in the Blender 3D viewport, I found that they were easier to operate than I expected, and I felt motivated. I've decided to master Bezier curve manipulation techniques.


縄文遺構3Dモデル化プロジェクトを進めるために、Blender基礎技術習得に励んでいます。その中でBlender3Dビューポートでべジェ曲線に色を付けてたところ、思いのほか操作しやすくなり、「やる気」が湧き出てきました。べジェ曲線操作技術を極めることにします。

1 Blender3Dビューポートにおけるべジェ曲線の表現


オブジェクトモード デフォルトのべジェ曲線(非選択)表現(黒線でほとんど見分けられない)


オブジェクトモード 新たに塗色したべジェ曲線(非選択)表現


オブジェクトモード デフォルトのべジェ曲線(選択)表現


編集モード 新たに塗色したべジェ曲線(非選択)表現


編集モード デフォルトのべジェ曲線(選択)表現

オブジェクトモード、編集モード共に、べジェ曲線(カーブ)は非選択デフォルトでは黒色となり、背景と識別しにくく、とても扱いづらいものです。選択すると判りやすくなるので、これまでは非選択での見にくさは無意識に我慢してきました。しかし、べジェ曲線の扱いについて意識的に学習を深める中で、思い切って色を変えてみました。オブジェクトモードでは黄緑に、編集モードでは水色にしました。色を変えた途端に選択していなくてもその存在が明瞭に判るようになりました。

同時に、べジェ曲線の扱いをとことん極めてみたいという欲望が生まれました。「やる気」が突然湧き出して来たということです。自分の心理機構がかなり単純であることに驚きました。その心理分析ははいつか深めることにして、今は何はともあれべジェ曲線の特性を極めて、縄文遺構3Dモデル作成技術の基礎を構築することにします。


Blenderでのべジェ曲線色変更画面

プリファレンス→テーマ→3Dビューポート→ワイヤーフレーム(及びワイヤ編集)

2 べジェ曲線の操作技術メモ

最近確認したべジェ曲線操作技術をメモします。

2-1 べジェ曲線を閉じる(ループをつくる)

2-1-1 Bezier Toolkitによる操作

自分はべジェ曲線はアドオンBezier Toolkitを使って描いています。このアドオンの操作では閉じる時にはALTキーを押しながらクリックすると最短にある頂点にスナップして、べジェ曲線を閉じることができます。

2-1-2 Fキーによる辺連結

既にある開いたべジェ曲線を閉じるときには両端の頂点を選択してFキーを押せば、閉じることができます。

2-2 べジェ曲線を開く

2-2-1 辺の削除

閉じたべジェ曲線の隣接する2つの頂点を選択して、X→セグメントで2つの頂点の間の辺が削除されます。

2-2-2 アクティブスプラインの操作

べジェ曲線が選択された状態で、「オブジェクトデータプロパティ→アクティブスプライン→ループのUチェックを外す」操作をすると、閉じたべジェ曲線の一部の辺が表示されなくなり、開いたべジェ曲線になります。Uチェックを入れると再び閉じたべジェ曲線になります。

この操作の説明はBlenderマニュアルにあるのですが、自分にはその意味がさっぱり判りません。Blender初心者の醍醐味(?)です。なぜそのような機能があるのかという意味が判らないのは初心者である今だけだと思います。

2-3 べジェ曲線を分割する

2-3-1 コピー、削除の繰り返し

べジェ曲線を複数コピーして、それぞれ別の場所の複数頂点を選択してX→セグメントで削除すれば、結果としてべジェ曲線を分割できます。

2-3-2 Bezier Toolkitによる操作

閉じたべジェ曲線が1つの平面に存在している場合、その同じ平面に別の閉じたべジェ曲線を描くいてその二つに交点があると、2つのべジェ曲線は交点により切断されてバラバラの開いたべジェ曲線になります。不用なべジェ曲線を削除すれば、当該べジェ曲線を分割したことになります。Illustratorのパスファインダーのような機能がBezier Toolkitにあるということです。

2-4 ハンドルタイプの変更

縄文遺構を実測図からトレースする時は操作の単純化、正確化のためにべジェ曲線ハンドルタイプをベクトル(カクカクの線分)にしています。モデリングのためのメッシュ化ではハンドルタイプを整列にして滑らかな線分で遺構を表現することにします。

2-5 Z座標値の補間

自分は現在、数本の閉じたべジェ曲線から、アドオンCurves To Meshでメッシュ生成することを3Dモデル作成の基本としています。この操作になかで、閉じたべジェ曲線のZ座標値の補間が重要です。その補間方法について、BlenderPythonスクリプトを作成したましたので、次の記事で紹介します。

2027.07.11記事「縄文遺構実測図の縁取線(地形変換線)の高度線形補間Blenderアドオン