こんにちは、ISDです。
今週(2025.12.11)、ChatGPTの 5.2 が発表されましたね。
11月に 5.1 にアップデートされたばっかりなのに、マジか!
しかも、コーディング精度がかなり向上しているらしいですね。
私の環境ではまだ使えませんが、順次反映されるとのことなので、触れるようになるのが楽しみです。
2つの3D面のエッジをスワップする
今回のお題はこれです。
「2つの3D面のエッジをスワップする」
「3D面のエッジをスワップ?」って、言葉でいうと伝わりにくいですが、図でいうとこうです。

3Dのモデリングでは、地形モデルなどを他のところからもらったりすることがありますが、
「TINの割り方をチョッといじりたいなぁ」
ということが、チョイチョイあります。
Civil3D には、TINサーフェスのエッジをスワップする EditSurfaceSwapEdge というコマンドがあります。

が、対象がTINサーフェスです。
他からもらった地形データは、大体メッシュか3D面になっていることが多いですね。
それに、AutoCAD にはこのコマンドはありません
「Civil3Dで処理すればいいんじゃない?」
と思うかもしれませんが、いちいちCivil3Dを起動してTINサーフェスに変換して……
とか、やりたくありません……面倒くさいし!
Civil3Dは、重いし、すぐ落ちるし、消せないレイヤたくさん作るし、なので……
私は、できるなら AutoCAD だけで済ませたい!
というわけで、オリジナルコマンド作成します。
ChatGPTでオリジナルコマンドを作成する
では、ChatGPT にお願いしてみようと思います。
が、いっぺんに出来るか不安なので、
まずは、準備段階として
「3D面の各辺を線分に変換する」
というお題で、小手調べです。
「3D面の各辺を線分に変換する」
投げたプロンプトはこちら
AutoCADで 以下の仕様のオリジナルコマンドを作成したい。
使用言語はAutolisp
###
3D面の各辺を線分に変換する
ChatGPT の返答がこちら
こんな感じのオリジナルコマンドでどうでしょう?
選択した 3D 面(3DFACE)の各辺を LINE にして作図する AutoLISP です。
コマンド仕様
- コマンド名:
3DF2LINE- 対象オブジェクト:3DFACE
- 処理内容:
- 3DFACE の 4 つの頂点座標を取得
- 頂点間を結ぶ線分(LINE)を作成
- 三角形 3DFACE(4点目が3点目と同じ場合)にも対応
- 元の 3DFACE と同じレイヤに線分を作成
ソースコード(AutoLISP)
;;; 3DFACE の各辺を LINE に変換するコマンド ;;; コマンド名:3DF2LINE (defun c:3DF2LINE ( / ss i ent ed v1 v2 v3 v4 lay) ;; 頂点座標を DXF データから取得する小関数 (defun get-vertex (data xcode ycode zcode) (list (cdr (assoc xcode data)) (cdr (assoc ycode data)) (cdr (assoc zcode data))) ) ;; 2点から LINE を作成する小関数 (defun make-line (p1 p2 lay) (entmakex (list '(0 . "LINE") (cons 8 lay) ;; レイヤ (cons 10 p1) ;; 始点 (cons 11 p2) ;; 終点 ) ) ) (prompt "\n3D面(3DFACE)を選択してください。") ;; 3DFACE のみを選択 (if (setq ss (ssget '((0 . "3DFACE")))) (progn (setq i 0) (while (< i (sslength ss)) (setq ent (ssname ss i) ed (entget ent) lay (cdr (assoc 8 ed)) ;; レイヤ名 v1 (get-vertex ed 10 20 30) v2 (get-vertex ed 11 21 31) v3 (get-vertex ed 12 22 32) v4 (get-vertex ed 13 23 33) ) ;; 辺 v1-v2, v2-v3 は必ず作成 (make-line v1 v2 lay) (make-line v2 v3 lay) ;; 3DFACE が三角形か四角形かを判定 ;; 三角形の場合:v3 = v4 になっている (if (not (equal v3 v4 1e-8)) (progn ;; 四角形:v3-v4, v4-v1 を作成 (make-line v3 v4 lay) (make-line v4 v1 lay) ) (progn ;; 三角形:v3-v1 を作成 (make-line v3 v1 lay) ) ) (setq i (1+ i)) ) (prompt "\n3D面の辺を線分に変換しました。") ) (prompt "\n3DFACE が選択されませんでした。") ) (princ) )
使い方
- 上のコードをテキストファイルにコピーして、
3DF2LINE.lspなどの名前で保存。- AutoCAD で
APPLOADコマンド →3DF2LINE.lspを読み込み。- コマンドラインに
3DF2LINEと入力 → Enter。- 「3D面(3DFACE)を選択してください。」と出るので、3DFACE を選択して Enter。
- その面の各辺が LINE として作図されます(同じレイヤ上)。
もし
- 重複する辺(共有エッジ)の生成を避けたい
- 別レイヤに線だけまとめて描きたい
などの要望があれば、そのバージョンも作れます。
では、試してます。
ChatGPT が出してきたコードを AutoCAD のコマンドラインにそのままコピペします。
すると、3DF2LINE という新しいコマンドが出来たようです。
コマンドを試してみたところ……
エラーが出ました。

エラーがでる
エラーがでました。
まあ、想定内です……
VSCODEで内容を見てみると、線分を生成する関数で、座標のDXFコードのリスト構造がおかしなことになってますね。

(10 -1.50768 2.73502 0.0) で渡さないといけない所を
(10 (-1.50768 2.73502 0.0) nil nil)として渡しています。
コードを追って見てみると、get-vertex という関数がきちんと機能していないっぽいです。
3D面の各頂点の座標を取得する個所ですが、これではダメですね
v1 (get-vertex ed 10 20 30) v2 (get-vertex ed 11 21 31) v3 (get-vertex ed 12 22 32) v4 (get-vertex ed 13 23 33)
ちなみに、今回テストした3D面のDXFコードは、以下
(-1 . <図形名: 21ca5985ba0>)
(0 . “3DFACE”)
(330 . <図形名: 21c9bd3e1f0>)
(5 . “E042”)
(100 . “AcDbEntity”)
(67 . 0)
(410 . “Model”)
(8 . “0”)
(100 . “AcDbFace”)
(10 -1.50768 2.73502 0.0) ; 第1頂点の座標
(11 -1.95095 0.194393 0.0) ; 第2頂点の座標
(12 1.80264 -1.81946 0.0) ; 第3頂点の座標
(13 1.80264 -1.81946 0.0) ; 第4頂点の座標
(70 . 0)
3D面のDXFエンティティには
10,11,12,13 というコードはあるけど、
20,21,22,23,30,31,32,33 はありません。
どうやら、ChatGPTは、3D面のエンティティを
「多分こうなっているだろう」
と想定して、結果間違ってしまったようです。
ここは、シレっと ヒントだけ出して
ChatGPTが動作するコードを出してくるか見てみることにします。
エラーの修正
ということで、投げたプロンプトがこちら
get-vertex が上手く機能していないようです。
3DFACEのDXFのフォーマットをよく確認して、再検討してください。
ChatGPTの返答がこちら
おっしゃる通りで、前回の
get-vertexの考え方が 3DFACE の DXF 仕様と合っていませんでした。
3DFACE の頂点は 10/11/12/13 の各コードに 3D座標のリストが そのまま入っている 形式です。
3DFACE の DXF フォーマット(頂点部分)
(entget)すると、3DFACE は概ねこんな感じのリストになります:( (0 . "3DFACE") (8 . "レイヤ名") (10 x1 y1 z1) ; 1頂点目 (11 x2 y2 z2) ; 2頂点目 (12 x3 y3 z3) ; 3頂点目 (13 x4 y4 z4) ; 4頂点目(3角面の場合は3頂点目と同じ) (70 . フラグ) ; 非表示エッジなどのビットフラグ ... )つまり、LINE と同じように
(10 x1 y1 z1)が 1 頂点目の 3D点(11 x2 y2 z2)が 2 頂点目の 3D点
…という形式です。なので、前回のように「10/20/30 で X/Y/Z をバラバラに取る」必要はなく、
(cdr (assoc 10 ed))だけで[x1 y1 z1]の点が取れます。
ここがget-vertexがうまく動いていなかった原因です。
修正版:3DFACE → LINE 変換コマンド
- コマンド名:
3DF2LINE- 仕様:
- 3DFACE の 4 頂点 (10,11,12,13) からエッジを作成
- 三角面(v3 = v4)と四角面の両方に対応
- 元の 3DFACE と同じレイヤに線を作成
- DXF の形式に合わせて頂点を取得し直し
;;; 3DFACE の各辺を LINE に変換するコマンド(DXF 仕様修正版) ;;; コマンド名:3DF2LINE (defun c:3DF2LINE ( / ss i ent ed lay v1 v2 v3 v4 flag) ;; 2点から LINE を作成する小関数 (defun make-line (p1 p2 lay) (entmakex (list '(0 . "LINE") (cons 8 lay) ;; レイヤ (cons 10 p1) ;; 始点 (cons 11 p2) ;; 終点 ) ) ) ;; 2次元リストなら z=0 を付けて3次元化(安全策) (defun fix-pt (pt) (if (= (length pt) 2) (append pt (list 0.0)) pt ) ) (prompt "\n3D面(3DFACE)を選択してください。") ;; 3DFACE のみを選択 (if (setq ss (ssget '((0 . "3DFACE")))) (progn (setq i 0) (while (< i (sslength ss)) (setq ent (ssname ss i) ed (entget ent) lay (cdr (assoc 8 ed)) ;; レイヤ v1 (fix-pt (cdr (assoc 10 ed))) ;; 1頂点目 v2 (fix-pt (cdr (assoc 11 ed))) ;; 2頂点目 v3 (fix-pt (cdr (assoc 12 ed))) ;; 3頂点目 v4 (fix-pt (cdr (assoc 13 ed))) ;; 4頂点目(3角ならv3と同じ) flag (cdr (assoc 70 ed)) ;; エッジ非表示フラグ(使うなら) ) ;; 必須の辺:v1-v2, v2-v3 (make-line v1 v2 lay) (make-line v2 v3 lay) ;; 三角形 or 四角形を判定 (if (not (equal v3 v4 1e-8)) (progn ;; 四角形:v3-v4, v4-v1 を作成 (make-line v3 v4 lay) (make-line v4 v1 lay) ) (progn ;; 三角形:v3-v1 を作成 (make-line v3 v1 lay) ) ) (setq i (1+ i)) ) (prompt "\n3D面の辺を線分に変換しました。") ) (prompt "\n3DFACE が選択されませんでした。") ) (princ) )
補足:非表示エッジ(フラグ70)も考慮したい場合
DXF のグループコード 70 には、
「どのエッジが不可視か」を示すビットフラグが入っています:
- ビット1 = 辺1 (v1–v2) が不可視
- ビット2 = 辺2 (v2–v3) が不可視
- ビット4 = 辺3 (v3–v4) が不可視
- ビット8 = 辺4 (v4–v1) が不可視
これを考慮して「見えているエッジだけ LINE にしたい」場合は、
たとえばこんな風に少し変えることができます:;; フラグを見てエッジを作る小関数 (defun make-edge (p1 p2 lay flag bit) (if (or (null flag) (zerop (logand flag bit))) ; 該当ビットが立っていなければ描画 (make-line p1 p2 lay) ) ) ... ;; ループ内で: (make-edge v1 v2 lay flag 1) ;; edge1 (make-edge v2 v3 lay flag 2) ;; edge2 (if (not (equal v3 v4 1e-8)) (progn (make-edge v3 v4 lay flag 4) ;; edge3 (make-edge v4 v1 lay flag 8) ;; edge4 ) (make-edge v3 v1 lay flag 4) ;; 三角面時の3本目扱い )
もしよければ:
- ポリラインでまとめて出したい
- 既にあるエッジと重複する LINE は作りたくない
などの仕様も追加で組み込めます。
まずはこの修正版を試してみて、挙動を教えてもらえれば、さらに調整します。
どうやら、3D面のDXFエンティティ構造は理解した?ようです。
新しいコードでテストしてみたところ、きちんと動作しているようです。

「2つの3D面のエッジをスワップする」
では、ウォーミングアップは終了ということで、
つぎは、「2つの3D面のエッジをスワップする」
いってみましょう。
ChatGPTに投げたプロンプトはこちら
AutoCADで 以下の仕様のオリジナルコマンドを作成したい。
使用言語はAutolisp
### 概要
「2つの3D面の共有エッジをスワップするコマンド」
A,B,C,Dの4点があり、 点A,B,C から成る三角形①と点C,D,Aから成る三角形②がある。
三角形①と三角形②の共有のエッジを変更したい
### 手順
①2つの3D面を選択する
②2つの3D面の共有のエッジを変更する。
③共有エッジが無かったら何もしない
今回は少し、細かく指示してみました。
これで伝わるかな~?
(私は、Civil3Dのメニューで初見の時「エッジをスワップって何?」ってなりましたけど……)
ChatGPTの返答がこちら
AutoLISP で「2つの3D面の共有エッジをスワップする」コマンドを作ってみました。
2つの 3DFACE を選ぶと、共有しているエッジを検出して、その反対側の対角線に張り替える処理をします。
動作仕様(おさらい)
- 3DFACE を 2つだけ選択する
- 2つの 3DFACE が ちょうど 1 本のエッジを共有している場合
→ その共有エッジを削除し、もう一方の対角線で 3DFACE を張り直す- 共有エッジが見つからない場合
→ 何もしない(メッセージだけ表示)
コマンド名
SWAP3Fにしてあります(お好みで変えてください)。
ソースコード
;;; 2つの3DFACEの共有エッジをスワップ(対角線を入れ替え) ;;; コマンド名:SWAP3F (vl-load-com) (defun get-3dface-verts (ent / ed lst uniq) ;; 3DFACE の頂点リスト(重複を除いて最大4点)を取得 (setq ed (entget ent)) (setq lst (list (cdr (assoc 10 ed)) (cdr (assoc 11 ed)) (cdr (assoc 12 ed)) (cdr (assoc 13 ed)) ) ) ;; 同じ座標の重複を除外(3角形の場合は 3 点になる) (setq uniq '()) (foreach p lst (if (and p (not (member p uniq))) (setq uniq (append uniq (list p))) ) ) uniq ) (defun tri-edges (vs / a b c) ;; 三角形の3エッジを [[p0 p1] [p1 p2] [p2 p0]] の形で返す ;; vs は少なくとも3点あることを想定 (setq a (nth 0 vs) b (nth 1 vs) c (nth 2 vs) ) (list (list a b) (list b c) (list c a) ) ) (defun same-edge-p (e1 e2 / p1 p2 q1 q2) ;; 方向を無視して同じエッジかどうかを判定 (setq p1 (car e1) p2 (cadr e1) q1 (car e2) q2 (cadr e2) ) (or (and (equal p1 q1 1e-9) (equal p2 q2 1e-9)) (and (equal p1 q2 1e-9) (equal p2 q1 1e-9)) ) ) (defun make-3dface-like (base p1 p2 p3 / data common) ;; base と同じプロパティ(レイヤなど)を持つ 3DFACE を ;; 頂点 p1 p2 p3 で作成(4点目は三角形なので p3 を重複) (setq data (entget base)) (setq common (vl-remove-if '(lambda (x) (member (car x) '(0 10 20 30 11 21 31 12 22 32 13 23 33 71 72 73 74) ) ) data ) ) (entmakex (append (list '(0 . "3DFACE")) common (list (cons 10 p1) (cons 11 p2) (cons 12 p3) (cons 13 p3) ) ) ) ) (defun c:SWAP3F (/ ss e1 e2 v1 v2 edges1 edges2 shared-edge s1 s2 opp1 opp2 tmp) (princ "\n2つの3DFACEを選択してください。") (setq ss (ssget ":S" '((0 . "3DFACE")))) (cond ((null ss) (princ "\n選択がキャンセルされました。") ) ((/= (sslength ss) 2) (princ "\n3DFACEを2つだけ選択してください。") ) (T (setq e1 (ssname ss 0) e2 (ssname ss 1) v1 (get-3dface-verts e1) v2 (get-3dface-verts e2) ) ;; 3 点以上あるかチェック(念のため) (if (or (< (length v1) 3) (< (length v2) 3)) (princ "\nどちらかの3DFACEが三角形ではありません。") (progn (setq edges1 (tri-edges v1) edges2 (tri-edges v2) shared-edge nil ) ;; 共有エッジを探す (foreach ed1 edges1 (foreach ed2 edges2 (if (same-edge-p ed1 ed2) (setq shared-edge ed1) ) ) ) (if (null shared-edge) (princ "\n2つの3D面は共有エッジを持っていません。") (progn (setq s1 (car shared-edge) ; 共有頂点1 s2 (cadr shared-edge) ; 共有頂点2 ) ;; 各三角形の「共有頂点ではない」頂点を取得 → 対向頂点 (setq tmp (vl-remove-if '(lambda (p) (or (equal p s1 1e-9) (equal p s2 1e-9) ) ) v1 ) ) (setq opp1 (car tmp)) (setq tmp (vl-remove-if '(lambda (p) (or (equal p s1 1e-9) (equal p s2 1e-9) ) ) v2 ) ) (setq opp2 (car tmp)) (if (or (null opp1) (null opp2) (equal opp1 opp2 1e-9)) (princ "\n共有エッジの条件が正しくありません。") (progn ;; 元の2枚を削除 (entdel e1) (entdel e2) ;; 新しい対角線(opp1-opp2)で2枚の3DFACEを作成 ;; 面の向きは特に気にせず構成(必要なら順序は調整可) (make-3dface-like e1 s1 opp1 opp2) (make-3dface-like e1 s2 opp2 opp1) ) ) ) ) ) ) ) ) (princ) )
簡単な説明
get-3dface-verts
3DFACE から 最大4つの頂点を取り出し、同じ座標の重複を除いたリストを返します(通常は三角形で3点)。tri-edges'(p0 p1 p2)から((p0 p1) (p1 p2) (p2 p0))の 3本のエッジリストを作ります。same-edge-p
エッジ[A B]と[B A]を 同じエッジとみなす比較関数です。make-3dface-like
元の3DFACEのレイヤや色などのプロパティを引き継いだまま、
指定した3点で新しい3DFACEを作成します。c:SWAP3F
メインコマンド。
- 3DFACE を 2つ選択
- 共有エッジ(共通の2頂点から成るエッジ)を探す
- 各三角形から「共有頂点ではない」頂点を1つずつ取り出す → 2つの対向頂点
- 古い2枚を削除し、対角線を張り替えた2枚を作成
もし「選択方法を1枚ずつクリックで選びたい」「面の表裏の向き(法線方向)を維持したい」など追加仕様があれば、その前提で調整版も作れます。
おぉ、何か伝わったみたいです。
「エッジをスワップする」
↓
「共有しているエッジを検出して、その反対側の対角線に張り替える」
と、こちらの意図を、きちんと読み取ってくれているようです。
(そうか、反対側の対角線に張り替えるといえばいいのか……)
テストした結果、きちんと動作しているようです。

離れている2つの3D面を選択すると、


共有エッジを持ってないと判断して、何もしないで終了します。
いいじゃん!
まとめ
今回は、
「2つの3D面の共有エッジをスワップする」
という、少しは実用性のある?オリジナルコマンドを作成してみました。
エラーも出ましたが、エラーの内容を ChatGPT にフィードバックして原因を究明させれば
デバッグも可能です。
あとは、特に検証はしていないですが、
最後のプロンプトはもう少し内容を端折っても通ったかもしれませんね。
しかし、プロンプトはやりたいことをなるべく具体的・詳細に書いてあげた方が良い結果につながるような気がします。
しかし、本当に侮れませんね、ChatGPT。
とても、面白いおもちゃを見つけた気分です。


コメント