2025年5月14日水曜日

技術資料 Blender閉じた辺ループのブリッジが合理的に行われる原理

 Technical information: The principle behind Blender's closed edge loops bridging


I use closed edge loops bridging as the main tool for creating 3D shell models in Blender. I've come to understand why bridging is performed so rationally, so I've made a note of it. Blender is performing complex calculations behind the scenes.


Blenderで貝層3Dモデルを作成するメインツールとして、閉じた辺ループのブリッジを活用しています。なぜ合理的にブリッジが行われるか理解できましたので、メモしました。Blenderは裏で複雑な計算をしています。

1 閉じた辺ループのブリッジの例


Blender3Dビューポートに配置された2つの閉じた辺ループ


辺ループのブリッジで中空3Dモデルが描画された様子


2 閉じた辺ループのブリッジが合理的に行われる原理


閉じた辺ループのブリッジが合理的に行われる原理

A0とB0の距離、A1とB1の距離・・・の組合わせの総延長距離r1

A0とB1の距離、A1とB2の距離・・・の組合わせの総延長距離r2

・・・

可能な全ての組合わせ(反転も含める)の総延長距離rを比較して「対応頂点間距離の合計が最小になる組合わせ(シフト)」を自動選択して、その組合わせで立体面を描画しています。

この例ではA0とB0の距離、A1とB1の距離・・・の組合わせの総延長距離が最小となり、その組合わせで立体面が描画されています。

3 出典

ChatGPTの次の回答によりました。

……………………………………………………………………

Blender の Bridge Edge Loops は、内部的には以下のような手順で「閉じた辺ループ同士」の頂点対応を決めています。

1. ループの検出

   選択した辺をたどって、同じ辺を一周するまで連続する辺を辿り、閉じたループとして頂点リストを列挙します(どこから始めてもループは円環状に扱われる) ([docs.blender.org][1])

2. デフォルト対応づけ (Twist=0 の場合)

   頂点数が同じ2つのループ A と B があるとき、A[i]→B[i] の順で対応づけを行います。ループは環状リストなので、たとえば A[0] を B のどの頂点と対応させるかは「シフト量」によって決まりますが、Twist=0 ではループをずらさずにそのまま 1:1 マッピングします ([docs.blender.org][1])

3. Twist パラメータによるオフセット

   UI の「Twist」スライダーを動かすと、B ループ全体を環状に何ステップずらすかを指定できます。これによって、A[0]→B[k] のように対応関係を回転させられます ([Superhive (formerly Blender Market)][2])

   実際には、Blender は可能なすべてのシフト(およびループの反転)を評価し、「対応頂点間距離の合計が最小になるシフト」を自動選択していると考えられます。

4. ブリッジの生成

   対応づけが確定したら、A[i] と B[i+シフト] をエッジでつなぎ、必要に応じて中間ループを挿入(Number of Cuts)、Interpolation や Smoothness の設定を反映して面を張ります ([docs.blender.org][1])

このように、橋渡しされる2つの閉じたループは「環状リストとして順序付け」→「同じインデックス同士で結ぶ(必要ならシフト)」→「面を生成」という原理で、常に“ずれなく”合理的な対応づけが行われます。

[1]: https://docs.blender.org/manual/en/latest/modeling/meshes/editing/edge/bridge_edge_loops.html?utm_source=chatgpt.com "Bridge Edge Loops - Blender 4.4 Manual"

[2]: https://blendermarket.com/products/curves-to-mesh/docs?utm_source=chatgpt.com "Curves To Mesh - Blender Market"

……………………………………………………………………

4 メモ

貝層3Dモデルを作成するツールとして、当初はBsurfaces機能を使っていたのですが、Bsurfaces機能は場面により使えないことに気が付き、汎用的に使えるのは辺ループのブリッジ機能であることに気が付き、着目しています。

辺ループのブリッジをgeometry nodesで表現しようと大格闘しているのですが、超膨大で超複雑なノードを組まないとできないことを知りました。(物事を簡単に処理するためにgeometry nodesがあると考えていたのですが、素手の操作で簡単な事柄を、超膨大・超複雑なgeometry nodesで表現しても、その意義はありません。[それを1つのノードにまとめたものが有料アドオンの中に含まれているので、それは意義があります。])

このような検討の中で、そもそも閉じた辺ループのブリッジの原理はなんなのかという素朴な疑問が生まれ、ChatGPTに質問して、回答を得た次第です。

2025年5月12日月曜日

遺物データベースの3D空間表示用Blenderアドオン自作

 I made a Blender add-on for displaying relic databases in 3D space.


I made an add-on for a database of relics with 3D coordinates that displays relics by classification in the Blender3D viewport. It feels like the database itself is imported into Blender, which is more convenient than importing data using BlenderPython scripts.


3D座標付遺物データベースについて、その分類毎に遺物をBlender3Dビューポートに表示するアドオンを自作しました。データベースそのものをBlenderに取り込んだ感覚になり、BlenderPythonスクリプトを使ったデータ取り込みより便利です。

1 遺物データベースの3D空間表示用Blenderアドオン Artifact Class Importer


遺物データベースの3D空間表示用Blenderアドオン Artifact Class Importer の説明

・ID、XYZ座標、分類から構成される5列のcsvファイルを読み込みます。

・ブラウザからcsvファイルを選択できます。

・遺物IDを名称とするオブジェクト(5㎝×5㎝×5㎝立方体)が3D空間に生成され、アウトライナーの特設コレクションに格納されます。

・全て表示および14分類のボタンにより、それぞれの分類毎にオブジェクトが3D空間に配置されます。


アドオンのパネル画面


アドオンのプロパティ


アドオンファイルの記述

2 ボタン別表示の様子


土器・土製品


石器・石


貝製品


骨・歯

3 感想

Blenderにデータベース情報を格納する能力があることを実感しました。

アドオン作成はChatGPTの支援を活用して、数回の修正を繰り返して実現しました。その途中で、本当にデータベース情報を格納できるのだろうかと疑問になり、ChatGPTに次の質問をしたほどです。

今後アドオンパネルを有効活用することにより、データベースソフト(postgreSQLなど)ではなく、Blenderで直接分析して、即3D空間表示するという新しい展望が開きました。

……………………………………………………………………

(この記事のアドオンの元となった別アドオンでのやりとりです。)

●私

再度試す前に、教えてください。今回読み込むcsvファイルにはIDと座標以外のデータベース情報が含まれています。その情報をBlenderが受け取り内部に格納することができると考えて間違いないか確認したいです。

●ChatGPT

はい、その認識で問題ありません。

Blender のオブジェクトには “カスタムプロパティ” として任意のキー/値ペアを内部に保持できます。今回のアドオンでも、以下のようにして

obj["class"] = class_v

obj["size"]  = size_v

obj["color"] = color_v

と書くことで class・size・color をオブジェクト内に格納していますが、CSV の他の列(例えば「発見年」や「遺跡名」など)があれば、同様に

for key, val in row.items():

    if key not in ("ID", "X", "Y", "Z"):

        obj[key] = val

とすれば、すべてのデータベース情報を obj["発見年"]、obj["遺跡名"] として取り込めます。

取り込んだプロパティは Python コンソールから bpy.data.objects["ID名"]["発見年"] のようにアクセスできますし、プロパティパネルに表示させることも可能です。

──つまり、ID/座標以外の列もカスタムプロパティとして完全に保持できます。追加で取り込みたいフィールドがあれば教えてください。

……………………………………………………………………


2025年5月11日日曜日

技術資料 貝層3Dモデルの接合

 Technical information: Joining shell layer 3D models


I confirmed how to join two shell layer 3D models that are in contact with each other. A 3D model is created by sharing the shell layer cross section line of the contact area. The outer surfaces of the contacting shell layer 3D models are joined by overlapping them as identical objects.


接触して存在する2つの貝層3Dモデルの接合方法を確認しました。接触部分の貝層断面線を共有して3Dモデルを作成します。接触存在する貝層3Dモデルのそれぞれの外面が同一のものとして重なることによって接合しています。

1 隣接する貝層3Dモデルの断面線を利用した貝層断面線


隣接貝層3Dモデルの断面線を利用した貝層断面線


隣接貝層3Dモデルの断面線利用部分

2 辺ループのブリッジによる3Dモデル作成


辺ループのブリッジによる3Dモデル作成

3 隣接3Dモデルとの接合確認


隣接3Dモデルとの接合確認


隣接3Dモデルとの接合確認

4 メモ

隣接する貝層3Dモデルの部分を利用することにより、隣接貝層3Dモデルとの接合が正しくできるようになりました。判ってみれば当たり前のことです。記事にするほどの価値があるかどうか気になります。しかし、数か月前はどうしたら2つの3Dモデルを吸着的に接合できるのか毎日考えていたのですから、不思議です。

技術資料 Blenderでの貝層3Dモデル作成実務

 Technical Document: Practical Use of Creating a 3D Shell Layer Model in Blender


I made a note of the practical use of creating a 3D shell layer model from two loop cross-section lines in Blender. By working while displaying the vertex index numbers of the edge loops on the 3D viewport screen, the efficiency was improved. A 3D model is created by bridging the edge loops.


Blenderで、2つのループ断面線から貝層3Dモデルを作成する実務をメモしました。3Dビューポート画面で辺ループの頂点インデックス番号を表示させながら作業することで、効率化が実現しました。辺ループのブリッジにより3Dモデルを作成します。

1 貝層3Dモデル作成実務の模式


貝層3Dモデル作成実務の模式

2 断面図の3Dビューポート追加

断面図を所定の位置に追加します。


断面図を追加した様子

3 べジェ曲線による主要頂点トレース

自分の場合アドオン Flexi Draw Bézierによりべジェ曲線(ハンドルタイプはベクター)で主要頂点をクリックして辺ループをトレースします。クリックだけでトレースできるので楽です。


べジェ曲線による辺ループのトレースの様子

4 メッシュ化

辺ループをメッシュ化し、編集モードに移動します。

メッシュ編集モードオーバーレイのインデックスをチェックすると、頂点にインデックス番号が3Dビューポート画面に表示されるようになります。


頂点インデックス番号表示方法


主要頂点を結んだ辺ループの様子 0~6のインデックスが表示されている

5 細部形状作図

主要頂点以外の細部形状を作図します。Ctrl+Rで辺に頂点を生成して、それをdragで移動して作図します。


細部形状作図の様子

6 主要頂点間の頂点数調整

主要頂点間の頂点数を同一にすることが必須なので、対応する別の断面で新たに加わった頂点数を追加します。追加方法はCtrl+Rで辺に頂点を生成して、それをdragで移動する方法です。頂点数はビューポートオーバレイの統計をチェックすると画面左上に表示されます。選択範囲の頂点数を表示させることもできます。


統計表示法


頂点数を調整した様子

7 2つの辺ループの統合

自分は、2つの辺ループを同時に表示したとき、別の色で認識しやすいように、ワイヤフレーム表示した時、別色になるようにしています。


2つの辺ループのワイヤフレームを別色で表示した様子


ワイヤフレームの塗色法

2つの辺ループの主要頂点間の頂点数が同一で、従って、全頂点数が同一であることを確認して、統合します。


2つの辺ループを統合した様子

8 辺ループのブリッジ

統合した2つの辺ループから辺ループのブリッジで中空3Dモデルを作成します。


辺ループのブリッジ適用の結果


中空3Dモデルと断面図


2025年5月6日火曜日

技術資料 Blenderでの貝層3Dモデル作成方法

 Technical information: How to create a 3D shell layer model in Blender


I have considered how to create a 3D shell layer model from a two-section shell layer distribution map in Blender. The most accurate method is to match the number of vertices to the main corresponding points and create it using "edge loop bridge".


Blenderで2断面の貝層分布図から貝層3Dモデルを作成する方法を検討しました。主要対応点までの頂点数を一致させて「辺ループのブリッジ」で作成する方法が正確です。

1 Blenderでの「辺ループのブリッジ」と「Bsurfaces機能」による貝層3Dモデル作成比較

Blenderでの「辺ループのブリッジ」と「Bsurfaces機能」による貝層3Dモデル作成比較

青モデル…「辺ループのブリッジ」利用で作成した3Dモデル

赤モデル…「Bsurfaces機能」利用で作成した3Dモデル

赤モデルは貝層断面が鋭角に変化するところで、対応すべき点がずれているので、不正確な3Dモデルとなっている。

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


3Dモデルの動画


3Dモデルの画像


3Dモデルの画像(ワイヤーフレーム)

2 メモ

Blenderで2断面の貝層分布図から貝層3Dモデルを作成する方法として、これまで「Bsurfaces機能」を利用してきました。しかし、鋭角に変化するような断面の場合では対応すべき点がずれるので、不正確な3Dモデルが生成されることが判明しました。主要対応点の始点からの頂点数を一致させて「辺ループのブリッジ」で作成すると、正確な貝層3Dモデル生成が可能であることに気が付きました。


2断面から貝層3Dモデルを作成する場合の事前準備

「Bsurfaces機能」では始点から等間隔に頂点を設定するので、鋭角に変化する貝層では不正確になります。鋭角に変化しない場合は「Bsurfaces機能」でも「辺ループのブリッジ」でも3Dモデル仕上がりはほとんど同じになります。

参考情報 アドオン「Bsurfaces」はBlenderバージョン4.4ではバグで動きません。


2025年5月3日土曜日

ツタンカーメン王の黄金のマスク切手 エジプト中国共同発行切手

 King Tutankhamun's Golden Mask Stamps, Egypt-China Joint Issue Stamps


I have a complete set of King Tutankhamun's Golden Mask stamps, jointly issued by Egypt and China. I am passionate about collecting archaeological stamps. My family criticizes me, saying that I am at an age where I have to get rid of all my possessions, and that I am still collecting stamps. However, you cannot collect stamps in the afterlife.


エジプトと中国が共同発行したツタンカーメン王の黄金のマスク切手が揃いました。考古学切手収集に熱を上げています。家族から全所有物処分年齢になって切手を集めてどうするのと責められています。しかし、あの世で切手収集はできません。

1 ツタンカーメン王の黄金のマスク切手 エジプト中国共同発行 エジプト側発行切手


ツタンカーメン王の黄金のマスク切手 エジプト中国共同発行 エジプト側発行切手

発行日:2001年10月12日

額面:30ピアストル

デザイナー:サイード・エル=バドラーウィ(Saeid El-Badrawy)

印刷所:カイロ郵便印刷所

2 ツタンカーメン王の黄金のマスク切手 エジプト中国共同発行 中国側発行切手


ツタンカーメン王の黄金のマスク切手 エジプト中国共同発行 中国側発行切手

発行日:2001年10月12日

発行番号:2001-20(スコットカタログ番号:3141–3142)

額面:80分

デザイナー:王虎鳴(Wang Huming)

サイズ:50×30mm

印刷方式:グラビア印刷

3 メモ

ツタンカーメン王の黄金のマスクは以前エジプト旅行で実見しています。しかし博物館規定により撮影できなかったので残念です。

ツタンカーメン王の黄金のマスク切手発行と同時に、エジプト中国共同で、三星堆の黄金仮面切手が発行されています。別記事で紹介します。

諸国が発行したツタンカーメン王の黄金のマスク切手がかなりの数で手元に集まっています。順次記事で紹介することにします。

考古学切手をやみくもに収集しているのですが、エジプト切手に考古意匠がダントツに多いので、エジプト切手収集にのめり込んでいます。

家族からは、全所有物を処分しようという年齢になって、切手を集めてどうするのと責められています。しかし、あの世では切手収集はできないと判断しています。従って、棺桶に入るまで、切手収集を満喫することにします。

2025年5月1日木曜日

2025年4月ブログ活動のふりかえり

 A look back at blogging activities in April 2025


I looked back on my activities in April 2025 for the blog "Walking the Hanami River Basin."

Continuing from March, I focused on examining the shell layer on the northern slope of the Ariyoshi Kita Shell Mound. The number of monthly blog posts was the lowest ever, but the deepening of 3D distribution analysis was remarkable.

ブログ「花見川流域を歩く」の2025年4月活動をふりかえりました。

3月に引き続き、有吉北貝塚北斜面貝層検討に集中しました。月間ブログ記事数は過去最低となりましたが、3D分布分析の深化は著しいものとなりました。

1 ブログ「花見川流域を歩く」

・2025年4月の記事数は10です。

・北斜面貝層検討を深める記事と関連するBlender技術に関する記事がほとんどとなりました。

・4月は3Dモデリング技術と考古学的検討の統合が進んだ月でした。

2 ブログ「花見川流域を歩く 自然・風景編」

・早朝散歩記事4編を書きました。

3 2025年4月活動の特徴

有吉北貝塚北斜面貝層の継続研究:斜面貝層の細区分と遺物(土器・骨・歯など)の3D分布分析に重点を置いた。

3Dモデル技術の活用深化:貝層と遺物の対応関係や密集度分布を3Dモデルで視覚的に検討。

祭場空間の仮説提起:イノシシ顎骨と狩猟道具の接近出土から、宗教・儀礼的空間の存在を推察。

Blender活用技術の進展:点群処理、geometry nodes、アドオン自作など技術面での試行と成果が顕著。

デジタル考古学の実践的応用:多数遺物の分布モデル作成を通じ、考古資料の可視化・分析手法を深化。

なお、web検索やChatGPTによる情報収集で考古遺物の3D空間分布とその分析事例の調査を繰り返し、参考になりそうな海外事例をいくつか収集しました。日本の事例は見つかりませんでした。存在していても、web空間には無いのだと思います。ChatGPTが日本の事例として紹介してきたものが、この花見川流域を歩くブログの過去記事だったので、笑っちゃいました。

4 2025年5月活動の展望

引き続き北斜面貝層検討を深める活動を進めることにします。3D分布分析技術をなんとか「モノにする」ことを目指します。

参考

ブログ「花見川流域を歩く」2025年4月記事

ブログ「花見川流域を歩く 自然・風景編」2025年4月記事


2025年4月 Sketchfabに投稿した3Dモデル


2025年4月 YouTubeに投稿した動画


2025年4月 ブログ「花見川流域を歩く」投稿記事に掲載した画像


2025年4月29日火曜日

有吉北貝塚北斜面貝層の斜面性貝層分層出土遺物の3D分布モデル

 3D distribution model of excavated artifacts from the sloping shell layer of the shell layer on the north slope of the Ariyoshi Kita Shell Mound


The sloping shell layer of the shell layer on the north slope of the Ariyoshi Kita Shell Mound was divided into six layers, and a 3D distribution model of the artifacts excavated from them was created. The distribution of artifacts traces the distribution of the shell layers, and it can be observed that the older shell layers are located above the slope and the newer shell layers are located below the slope.


有吉北貝塚北斜面貝層の斜面性貝層を6つに分層して、それらから出土した遺物の3D分布モデルを作成しました。遺物分布は貝層分布をトレースしていて、古い貝層から新しい貝層の順に斜面を下っている様子が観察できます。

1 有吉北貝塚北斜面貝層の斜面性貝層分層出土遺物の3D分布モデル

有吉北貝塚北斜面貝層の斜面性貝層分層出土遺物の3D分布モデル

場所:有吉北貝塚北斜面貝層検討空間(10断面と11断面に挟まれた空間)

遺物:CUBE(5㎝×5㎝×5㎝)で示す 白は白色貝層出土、褐色は褐色貝層出土

10断面(手前)と11断面(奥)の間隔は2m


参考 斜面性貝層細区分

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


3Dモデルの動画


3Dモデルの画像

2 斜面性貝層6分層別遺物分布


斜面性貝層6分層別遺物分布

6分層別に遺物分布を切り出して比較しました。

遺物分布は貝層分布をトレースしていて、古い貝層から新しい貝層に向かって斜面の上から下に移動している様子がわかります。斜面利用が上から始まり、下に移動しました。また遺物分布(貝層分布)の斜面全長に占める割合が小→大→小と変化していることも特徴です。貝殻投棄(貝層形成)と遺物投棄に関わる活動内容が時期によって変化している様子がこうした出土状況に投影されているのかもしれません。

3 メモ

分層別統計は次の記事に掲載しています。

2025.04.05記事「斜面貝層細区分と遺物3D分布モデル



2025年4月28日月曜日

Blenderのgeometry nodes機能習熟に取組む

 Learning Blender's geometry nodes


As a Blender beginner, I am engrossed in mastering the geometry nodes function. It is convenient because it allows you to freely and easily change the color, size, and shape of the original object and create convex hulls for 3D distribution models of many relics. It can also be registered as an asset and reused.


Blender初心者としてgeometry nodes機能習熟に熱中しています。多数遺物の3D分布モデルについて、元オブジェクト非破壊的に色や大きさや形状を変化させ凸包作成なども自由、気軽にできるので便利です。assetとして登録もでき、使いまわせます。

1 geometry nodes使用例 斜面性貝層出土遺物表現


geometry nodes使用例 斜面性貝層出土遺物表現


geometry nodes説明

有吉北貝塚北斜面貝層の斜面性貝層を6層に分層して、各層から出土した遺物の3D分布モデルR1~S3をつくりました。この6つの3D分布モデルについて色塗りして1枚平面のモディファイアとして表現しました。

必要に応じて色や大きさや形状や凸包作成などBlenderで出来る作業のほとんどができます。geometry nodesの「箱」(node=機能)を組合わせるだけで、作業が実現します。「箱」の組合わせだけですから、直接オブジェクトをいじらないので、自由で気軽で、やり直しが苦にならないで快適です。

2 asset登録可能

この例は別のBlenderファイルで作成した平面モディファイアをassetとして登録しておいたものです。新たに立ち上げたBlenderのアセットブラウザーから登録されたassetを見つけ、drag&dropで取り込んだものです。平面モディファイアを取り込むとその中で使われている6つの遺物3D分布モデル(R1~S3)も一緒に取り込まれました。

2025年4月27日日曜日

3D座標付き遺物リストのプロット用Blenderアドオン自作

 I made a Blender addon for plotting a list of relics with 3D coordinates


I made a Blender addon that plots a list of relics with 3D coordinates in Blender3D space. Until now I've been doing this with BlenderPython, but it's much more convenient to make it an addon. I was able to create the addon quickly with ChatGPT support.


3D座標付き遺物リストをBlender3D空間にプロットするBlenderアドオンを自作しました。これまではBlenderPythonで行ってきましたが、アドオンにした方がはるかに便利です。アドオン作成はChatGPT支援で直ぐにできました。

1 3D座標付き遺物リストのプロット用Blenderアドオン CSV Tools


3D座標付き遺物リストのプロット用Blenderアドオンの機能

csvファイルは単純に次の形式としました。

ID,X,Y,Z,TYPE

1,0,0,0,a

2,2,3,1,b

3,5,-2,4,c

csvファイルを選択できるようにしたので、便利です。

ID名(遺物名)のオブジェクトを作成し、TYPE名のコレクションに格納するので、多数遺物の扱いがしやすくなっています。

オブジェクトは0.05m×0.05m×0.05mのCUBEとして設定してあります。

アドオンづくりの楽しみという観点から言えば、色塗りを付加したり、オブジェクトの大きさや形状を変えたりといろいろ工夫はできるのですが、それらの機能を付加すると、自分の作業ではかえって煩雑になるので、それらからは試したうえで、撤退しました。

プロットしたものをgeometry nodesで凝った装飾を加える方が手軽で、自由で、効率的です。最初にプロットしたものに対して非破壊的にできます。

このアドオン作成はChatGPTの支援を受けました。

2 参考 自作アドオンのPythonスクリプト

以下のスクリプトをテキストファイルにして、拡張子をtxtからpyに変更するとPythonスクリプトになります。そのPythonスクリプトをBlenderアドオンとして読み込むと、アドオンCSV Toolsが使えるようになります。

bl_info = {
    "name": "CSVからCUBE配置ツール",
    "author": "ChatGPTアシスト",
    "version": (1, 1),
    "blender": (3, 0, 0),
    "location": "3Dビュー > サイドバー > CSV Tools",
    "description": "CSVファイルの座標にCUBEを配置し、TYPEごとにコレクションに整理するツール (ファイル選択対応)",
    "category": "Object",
}

import bpy
import csv
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator, Panel

# --- CSVからCUBEを配置するメイン処理 ---
def get_or_create_collection(name):
    if name in bpy.data.collections:
        return bpy.data.collections[name]
    else:
        new_collection = bpy.data.collections.new(name)
        bpy.context.scene.collection.children.link(new_collection)
        return new_collection

def create_cubes_from_csv(csv_file):
    with open(csv_file, newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            x = float(row['X'])
            y = float(row['Y'])
            z = float(row['Z'])
            type_name = row['TYPE']
            
            # Cube作成
            bpy.ops.mesh.primitive_cube_add(size=1, location=(x, y, z))
            obj = bpy.context.active_object
            obj.scale = (0.05, 0.05, 0.05)
            obj.name = f"Artifact_{row['ID']}"

            # 適切なコレクションに移動
            collection = get_or_create_collection(type_name)
            collection.objects.link(obj)
            bpy.context.scene.collection.objects.unlink(obj)

# --- オペレータークラス ---
class OBJECT_OT_import_csv_create_cubes(Operator, ImportHelper):
    bl_idname = "object.import_csv_create_cubes"
    bl_label = "CSVを読み込んでCUBEを配置"
    bl_options = {'REGISTER', 'UNDO'}

    filename_ext = ".csv"
    filter_glob: StringProperty(
        default="*.csv",
        options={'HIDDEN'},
    )

    def execute(self, context):
        create_cubes_from_csv(self.filepath)
        return {'FINISHED'}

# --- パネルクラス ---
class VIEW3D_PT_csv_tools(Panel):
    bl_label = "CSV Tools"
    bl_idname = "VIEW3D_PT_csv_tools"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'CSV Tools'

    def draw(self, context):
        layout = self.layout
        layout.operator(OBJECT_OT_import_csv_create_cubes.bl_idname, text="CSVファイルを選択して配置")

# --- 登録処理 ---
classes = (
    OBJECT_OT_import_csv_create_cubes,
    VIEW3D_PT_csv_tools,
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)

if __name__ == "__main__":
    register()