こんにちは、ISDです。
前回は、3Dポリラインの頂点座標を Excel に出力するオリジナルコマンドを作成しました。
そしたら次は、やはり Excel から頂点座標を読み取って3Dポリラインの頂点座標に反映したくなります。よね?
というわけで、次のお題はこれです。
「3Dポリラインの頂点座標をエクセルの数値をもとに変更する」
ChatGPTでオリジナルコマンドを作成する
では、ChatGPT にお願いしてみようと思います。
コマンドの概要は以下の通りとします。
「選択した1つの3Dポリラインについて、各頂点のXYZ座標を、Excelワークシートから読み取った値で置き換える。」
- Excel が起動していなければ、コマンド終了。
- 読み取る対象のシート名は”3DPL_頂点座標”。シートがなければコマンド終了。
- 3Dポリラインの頂点の座標を順番に1行ずつ読み込む
- 頂点のX座標は Aの列、Y座標は Bの列、Z座標は Cの列
コマンドの概要の他に、
AutoLISPのコードを書く際にChatGPTがやらかしそうなことを予防するため、あらかじめクギを刺しておくことにします。
追加して、前回のコマンド作成で、vlax-get-property で エラーになっていた Cells は使わず、Range を使うように指示します。
では、ChatGPTにプロンプトを投げます。
AutoCADで 以下の仕様のオリジナルコマンドを作成したい。
使用言語はAutolisp### コマンドの概要
選択した1つの3Dポリラインについて、各頂点のXYZ座標を、Excelワークシートから読み取った値で置き換える。
・EXCEL が起動していなければ、コマンド終了。
・読み取る対象のシート名は”3DPL_頂点座標”。シートがなければコマンド終了。
・3Dポリラインの頂点の座標を順番に1行ずつ読み込む、ただし1行目はヘッダー行
・頂点のX座標は Aの列、Y座標は Bの列、Z座標は Cの列### AutoLISP 禁則事項
以下は必ず守ること。
・予約定数に代入しない【厳禁】
t / T / nil / NIL / pi / PIなど
・組み込み関数名を変数名に使わない
if, list, car, cdr, min, max, command, entget, vlax-* など
・vlax-get-property で Cells(row,col) は使わない。Range(“A2”) の形に変換して書く
・Range / Worksheet / Workbook / Workbooks は必ず release
・Excel.Application の release は原則行わない(既存 Excel への影響を避けるため)
そして、ChatGPTの返答
以下は、指定仕様・禁則事項を守った 全体完成版コマンドです。
(Excel が起動していなければ終了/シートが無ければ終了/1行目ヘッダ/Range(“A2”)方式/Range等はrelease/Excel.Applicationは原則releaseしない)
コマンド名:3DPLXLS2VTX
- 3Dポリライン(AcDb3dPolyline)を1つ選択
- Excel(起動済み必須)のシート
"3DPL_頂点座標"の
- A列=X, B列=Y, C列=Z
- 2行目から(1行目はヘッダ)
- 頂点の座標を順番に置換(行数が足りない場合はそこで停止)
- 値が数値として読めない場合は その頂点は変更しない(ログ出す)
(vl-load-com) ;; ---------------------------- ;; logger ;; ---------------------------- (defun _3dplxls2vtx-log (msg) (princ (strcat "\n[3DPLXLS2VTX] " msg)) ) ;; ---------------------------- ;; 列番号→列名(1->A, 2->B, 27->AA ...) ;; ---------------------------- (defun _3dplxls2vtx-col->name (colNum / n r s) (setq n colNum) (setq s "") (while (> n 0) (setq r (rem (1- n) 26)) (setq s (strcat (chr (+ 65 r)) s)) (setq n (/ (- n r 1) 26)) ) s ) ;; ---------------------------- ;; Excel セル読み取り(Range("A2")) ;; - 返り値: Value2 の生値(nil/数値/文字列など) ;; - Range は必ず release ;; ---------------------------- (defun _3dplxls2vtx-read-cell (ws rowNum colNum / addr rng raw) (setq addr (strcat (_3dplxls2vtx-col->name colNum) (itoa rowNum))) (setq rng (vlax-get-property ws 'Range addr)) (setq raw (vlax-get-property rng 'Value2)) (vlax-release-object rng) raw ) ;; ---------------------------- ;; Excelの値→数値に寄せる(読めなければ nil) ;; - INT/REAL はそのまま ;; - STR は distof で解釈(失敗したら nil) ;; ---------------------------- (defun _3dplxls2vtx-read-num (ws rowNum colNum / v n) (setq v (_3dplxls2vtx-read-cell ws rowNum colNum)) (cond ((null v) nil) ((= (type v) 'INT) (float v)) ((= (type v) 'REAL) v) ((= (type v) 'STR) (setq n (distof v 2)) (if n n nil) ) (T nil) ) ) ;; ---------------------------- ;; Excel が起動している前提で取得(起動してなければ nil) ;; ---------------------------- (defun _3dplxls2vtx-get-running-excel ( / r app) (setq r (vl-catch-all-apply 'vlax-get-object (list "Excel.Application"))) (if (vl-catch-all-error-p r) nil r ) ) ;; ---------------------------- ;; ブック中からシート名一致を取得(無ければ nil) ;; - Worksheets/Sheet は必ず release(戻りとして返すシート以外) ;; ---------------------------- (defun _3dplxls2vtx-get-sheet-by-name (wb targetName / wss cnt idx ws found) (setq wss (vlax-get-property wb 'Worksheets)) (setq cnt (vlax-get-property wss 'Count)) (setq found nil) (setq idx 1) (while (and (<= idx cnt) (null found)) (setq ws (vlax-get-property wss 'Item idx)) (if (= (vlax-get-property ws 'Name) targetName) (setq found ws) (vlax-release-object ws) ) (setq idx (1+ idx)) ) (vlax-release-object wss) found ) ;; ---------------------------- ;; 3Dポリライン(旧形式 POLYLINE/VERTEX/SEQEND)頂点エンティティを順に走査 ;; - fn は (fn vertexEname index) の形で呼ぶ ;; ---------------------------- (defun _3dplxls2vtx-iter-3dpl-vertex-entities (plEnt fn / e ed etype idx) (setq idx 0) (setq e (entnext plEnt)) (while e (setq ed (entget e)) (setq etype (cdr (assoc 0 ed))) (cond ((= etype "VERTEX") (setq idx (1+ idx)) (fn e idx) (setq e (entnext e)) ) ((= etype "SEQEND") (setq e nil) ) (T (setq e (entnext e)) ) ) ) idx ) ;; ---------------------------- ;; VERTEX の 10座標を書き換え ;; - 失敗したら nil ;; ---------------------------- (defun _3dplxls2vtx-set-vertex-xyz (vtxEnt x y z / ed newEd oldP) (setq ed (entget vtxEnt)) (setq oldP (assoc 10 ed)) (if (null oldP) nil (progn (setq newEd (subst (cons 10 (list x y z)) oldP ed)) (entmod newEd) (entupd vtxEnt) T ) ) ) (defun c:3DPLXLS2VTX ( / entPick entName vlaObj objName xlApp xlBook xlSheet sheetName vtxEnt vtxIdx rowNum xVal yVal zVal changedCount ) ;; ============================ ;; Main ;; ============================ (_3dplxls2vtx-log "コマンド開始") ;; 1) Excel が起動しているか(起動してなければ終了) (setq xlApp (_3dplxls2vtx-get-running-excel)) (if (null xlApp) (progn (_3dplxls2vtx-log "Excel が起動していません。コマンド終了。") (princ) (exit) ) ) (_3dplxls2vtx-log "Excel 起動中を確認") ;; 2) ActiveWorkbook 取得(無ければ終了) (setq xlBook (vlax-get-property xlApp 'ActiveWorkbook)) (if (null xlBook) (progn (_3dplxls2vtx-log "ActiveWorkbook が取得できません。コマンド終了。") (princ) (exit) ) ) (_3dplxls2vtx-log "ActiveWorkbook を取得") ;; 3) 対象シート取得(無ければ終了) (setq sheetName "3DPL_頂点座標") (setq xlSheet (_3dplxls2vtx-get-sheet-by-name xlBook sheetName)) (if (null xlSheet) (progn (_3dplxls2vtx-log (strcat "対象シートがありません: " sheetName " / コマンド終了。")) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) (_3dplxls2vtx-log (strcat "対象シート取得: " sheetName)) ;; 安定化:Activate → ActiveSheet を取り直す (vlax-invoke-method xlSheet 'Activate) (vlax-release-object xlSheet) (setq xlSheet (vlax-get-property xlApp 'ActiveSheet)) ;; 4) 3Dポリライン選択 (setq entPick (entsel "\n3Dポリラインを1つ選択: ")) (if (null entPick) (progn (_3dplxls2vtx-log "選択がキャンセルされました。") (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) (setq entName (car entPick)) ;; 5) 3Dポリライン判定 (setq vlaObj (vlax-ename->vla-object entName)) (setq objName (vlax-get-property vlaObj 'ObjectName)) (_3dplxls2vtx-log (strcat "Selected ObjectName = " objName)) (if (not (wcmatch (strcase objName) "*3DPOLYLINE*")) (progn (_3dplxls2vtx-log "3Dポリラインではありません。コマンド終了。") (vlax-release-object vlaObj) (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) (vlax-release-object vlaObj) ;; 6) 頂点を順に読み替え(行=idx+1、1行目はヘッダ) (_3dplxls2vtx-log "Excel 2行目から A/B/C を読み、頂点座標を置換します") (setq changedCount 0) (_3dplxls2vtx-iter-3dpl-vertex-entities entName (function (lambda (vertexEname index / r x y z ok) (setq r (+ index 1)) ;; 1行目ヘッダなので 2行目から ;; 行が空なら終了扱いにしたい場合: ;; 今回は「x,y,z のどれかが nil」ならその頂点はスキップ(ログ)にする (setq x (_3dplxls2vtx-read-num xlSheet r 1)) (setq y (_3dplxls2vtx-read-num xlSheet r 2)) (setq z (_3dplxls2vtx-read-num xlSheet r 3)) (if (and x y z) (progn (setq ok (_3dplxls2vtx-set-vertex-xyz vertexEname x y z)) (if ok (progn (setq changedCount (1+ changedCount)) (if (= (rem index 20) 0) (_3dplxls2vtx-log (strcat " changed vertex " (itoa index) " ...")) ) ) (_3dplxls2vtx-log (strcat " 頂点更新失敗 index=" (itoa index))) ) ) (_3dplxls2vtx-log (strcat " 数値読取不可/空欄のためスキップ index=" (itoa index) " row=" (itoa r))) ) ) ) ) (_3dplxls2vtx-log (strcat "置換完了: changed vertices = " (itoa changedCount))) ;; 7) 後始末(Range/Worksheet/Workbook は release) (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) ;; Excel.Application は原則 release しない(既存Excelへの影響を避ける) (_3dplxls2vtx-log "コマンド終了") (princ) )
実務メモ(仕様に合わせて調整しやすい点)
- 行が足りない時の挙動
- 現状:値が読めない行は「その頂点だけスキップ」して続行(ログ出す)
- 「空行が出たらそこで終了」にしたいなら、
x y zのどれかが nil のときに 以降を止めるようにもできます(仕様次第)- 3Dポリラインの形式
- この実装は「旧形式(POLYLINE + VERTEX + SEQEND)」を想定(あなたの
AcDb3dPolylineログと整合)- もし “3D Polyline(軽量)/別形式” が混ざるなら分岐を追加します
必要なら次の一手として、
「Excelの行数と頂点数が一致しない場合はエラー終了(巻き戻し)」みたいな安全運転モードも入れられます。
何か、出してきました。
コマンドの実行とデバッグ
では、実際に試してみます。

適当な 3Dポリライン を用意し、前回の「3Dポリラインの頂点座標をエクセルに出力する」コマンドを使って、3Dポリラインの頂点座標を Excel に出力します。

テストとして、各頂点のZ座標の値を適当に変更してみます。

これで、準備はOK。
では、ChatGPTが書いてくれたコマンド 「3DPLXLS2VTX」を起動します。

おっと、エラーが出ました。
そういえば、今回のプロンプトで、「要所にログを仕込む」という指示を忘れていましたが、ChatGPTが過去のやりとりを覚えていてくれたようで、気を利かせてログを仕込んでくれています。
ログをChatGPTにフィードバックします。
エラーが出ました。
###
コマンド: 3DPLXLS2VTX
[3DPLVTX2XLS] コマンド開始
[3DPLVTX2XLS] Excel 起動中を確認
[3DPLVTX2XLS] ActiveWorkbook を取得
[3DPLVTX2XLS] 対象シート取得: 3DPL_頂点座標 3Dポリラインを1つ選択:
[3DPLVTX2XLS] Selected ObjectName = AcDb3dPolyline
[3DPLVTX2XLS] Excel 2行目から A/B/C を読み、頂点座標を置換します
; エラー: 関数が間違っています: (quote #)
原因はこれです:
; エラー: 関数が間違っています: (quote #<SUBR ... -lambda->)つまり
function/lambdaを渡すやり方が、あなたの環境の AutoLISP では受け付けられていません(Visual LISP の関数オブジェクトとして評価されてしまい、呼び出し側でfnとして実行できない)。なので設計を変えます。
修正方針
- 「コールバック関数(lambda)」を使わない
- 3Dポリラインの VERTEX を while で直接走査
- 各頂点に対して Excel の行(2行目~)を読み、
entmodで座標差し替えこれなら 古い/厳しめの AutoLISP でも確実に動きます。
修正版:全体完成版(lambda/function 不使用)
コマンド名は同じ
3DPLXLS2VTXです。(vl-load-com) ;; ---------------------------- ;; logger ;; ---------------------------- (defun _3dplxls2vtx-log (msg) (princ (strcat "\n[3DPLXLS2VTX] " msg)) ) ;; ---------------------------- ;; 列番号→列名(1->A, 2->B, 27->AA ...) ;; ---------------------------- (defun _3dplxls2vtx-col->name (colNum / n r s) (setq n colNum) (setq s "") (while (> n 0) (setq r (rem (1- n) 26)) (setq s (strcat (chr (+ 65 r)) s)) (setq n (/ (- n r 1) 26)) ) s ) ;; ---------------------------- ;; Excel セル読み取り(Range("A2")) ;; ---------------------------- (defun _3dplxls2vtx-read-cell (ws rowNum colNum / addr rng raw) (setq addr (strcat (_3dplxls2vtx-col->name colNum) (itoa rowNum))) (setq rng (vlax-get-property ws 'Range addr)) (setq raw (vlax-get-property rng 'Value2)) (vlax-release-object rng) raw ) ;; ---------------------------- ;; Excel値→数値(読めなければ nil) ;; ---------------------------- (defun _3dplxls2vtx-read-num (ws rowNum colNum / v n) (setq v (_3dplxls2vtx-read-cell ws rowNum colNum)) (cond ((null v) nil) ((= (type v) 'INT) (float v)) ((= (type v) 'REAL) v) ((= (type v) 'STR) (setq n (distof v 2)) (if n n nil) ) (T nil) ) ) ;; ---------------------------- ;; 起動中の Excel を取得(起動してなければ nil) ;; ---------------------------- (defun _3dplxls2vtx-get-running-excel ( / r) (setq r (vl-catch-all-apply 'vlax-get-object (list "Excel.Application"))) (if (vl-catch-all-error-p r) nil r) ) ;; ---------------------------- ;; ブック中からシート名一致を取得(無ければ nil) ;; ---------------------------- (defun _3dplxls2vtx-get-sheet-by-name (wb targetName / wss cnt idx ws found) (setq wss (vlax-get-property wb 'Worksheets)) (setq cnt (vlax-get-property wss 'Count)) (setq found nil) (setq idx 1) (while (and (<= idx cnt) (null found)) (setq ws (vlax-get-property wss 'Item idx)) (if (= (vlax-get-property ws 'Name) targetName) (setq found ws) (vlax-release-object ws) ) (setq idx (1+ idx)) ) (vlax-release-object wss) found ) ;; ---------------------------- ;; VERTEX の 10座標を書き換え ;; ---------------------------- (defun _3dplxls2vtx-set-vertex-xyz (vtxEnt x y z / ed oldP newEd) (setq ed (entget vtxEnt)) (setq oldP (assoc 10 ed)) (if (null oldP) nil (progn (setq newEd (subst (cons 10 (list x y z)) oldP ed)) (entmod newEd) (entupd vtxEnt) T ) ) ) (defun c:3DPLXLS2VTX ( / entPick entName vlaObj objName xlApp xlBook xlSheet sheetName e ed etype vtxIdx rowNum xVal yVal zVal ok changedCount ) ;; ============================ ;; Main ;; ============================ (_3dplxls2vtx-log "コマンド開始") ;; Excel 起動確認 (setq xlApp (_3dplxls2vtx-get-running-excel)) (if (null xlApp) (progn (_3dplxls2vtx-log "Excel が起動していません。コマンド終了。") (princ) (exit) ) ) (_3dplxls2vtx-log "Excel 起動中を確認") ;; ActiveWorkbook (setq xlBook (vlax-get-property xlApp 'ActiveWorkbook)) (if (null xlBook) (progn (_3dplxls2vtx-log "ActiveWorkbook が取得できません。コマンド終了。") (princ) (exit) ) ) (_3dplxls2vtx-log "ActiveWorkbook を取得") ;; Sheet (setq sheetName "3DPL_頂点座標") (setq xlSheet (_3dplxls2vtx-get-sheet-by-name xlBook sheetName)) (if (null xlSheet) (progn (_3dplxls2vtx-log (strcat "対象シートがありません: " sheetName " / コマンド終了。")) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) (_3dplxls2vtx-log (strcat "対象シート取得: " sheetName)) ;; 安定化:Activate → ActiveSheet 再取得 (vlax-invoke-method xlSheet 'Activate) (vlax-release-object xlSheet) (setq xlSheet (vlax-get-property xlApp 'ActiveSheet)) ;; 3Dポリライン選択 (setq entPick (entsel "\n3Dポリラインを1つ選択: ")) (if (null entPick) (progn (_3dplxls2vtx-log "選択がキャンセルされました。") (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) (setq entName (car entPick)) ;; 判定 (setq vlaObj (vlax-ename->vla-object entName)) (setq objName (vlax-get-property vlaObj 'ObjectName)) (_3dplxls2vtx-log (strcat "Selected ObjectName = " objName)) (vlax-release-object vlaObj) (if (not (wcmatch (strcase objName) "*3DPOLYLINE*")) (progn (_3dplxls2vtx-log "3Dポリラインではありません。コマンド終了。") (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) (princ) (exit) ) ) ;; 頂点走査しながら Excel を読む(1行目ヘッダ→2行目から) (_3dplxls2vtx-log "Excel 2行目から A/B/C を読み、頂点座標を置換します") (setq changedCount 0) (setq vtxIdx 0) (setq e (entnext entName)) (while e (setq ed (entget e)) (setq etype (cdr (assoc 0 ed))) (cond ((= etype "VERTEX") (setq vtxIdx (1+ vtxIdx)) (setq rowNum (+ vtxIdx 1)) ;; 2行目から (setq xVal (_3dplxls2vtx-read-num xlSheet rowNum 1)) (setq yVal (_3dplxls2vtx-read-num xlSheet rowNum 2)) (setq zVal (_3dplxls2vtx-read-num xlSheet rowNum 3)) (if (and xVal yVal zVal) (progn (setq ok (_3dplxls2vtx-set-vertex-xyz e xVal yVal zVal)) (if ok (progn (setq changedCount (1+ changedCount)) (if (= (rem vtxIdx 20) 0) (_3dplxls2vtx-log (strcat " changed vertex " (itoa vtxIdx) " ...")) ) ) (_3dplxls2vtx-log (strcat " 頂点更新失敗 index=" (itoa vtxIdx))) ) ) (_3dplxls2vtx-log (strcat " 数値読取不可/空欄のためスキップ index=" (itoa vtxIdx) " row=" (itoa rowNum))) ) ) ((= etype "SEQEND") (setq e nil) ) ) (if e (setq e (entnext e))) ) (_3dplxls2vtx-log (strcat "置換完了: changed vertices = " (itoa changedCount))) ;; 後始末(規約通り) (if xlSheet (vlax-release-object xlSheet)) (if xlBook (vlax-release-object xlBook)) ;; Excel.Application は原則 release しない (_3dplxls2vtx-log "コマンド終了") (princ) )
この修正で直る点
lambda/functionを一切使わないので、あなたの環境でも「関数が間違っています」系が出ません。- 走査も Excel 読みも while 直書きなので、デバッグがさらに楽です。
次に決めたい仕様(確認いらないならこちらでデフォルト採用します)
- Excel 側の行が途中で空欄になった場合
- 現状:その頂点は スキップして続行
- 代案:空欄が出たら そこで処理終了(以降は変更しない)
どっちが運用に合います?
修正コードを出してきたので、試してみます。

処理は進みましたが、Excel から値が読み取れていないですね。
さらに、フィードバックします。
Excel から数値が読み取れていません
###
コマンド: 3DPLXLS2VTX
[3DPLVTX2XLS] コマンド開始
[3DPLVTX2XLS] Excel 起動中を確認
[3DPLVTX2XLS] ActiveWorkbook を取得
[3DPLVTX2XLS] 対象シート取得: 3DPL_頂点座標 3Dポリラインを1つ選択:
[3DPLVTX2XLS] Selected ObjectName = AcDb3dPolyline
[3DPLVTX2XLS] Excel 2行目から A/B/C を読み、頂点座標を置換します
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=1 row=2
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=2 row=3
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=3 row=4
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=4 row=5
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=5 row=6
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=6 row=7
[3DPLVTX2XLS] 数値読取不可/空欄のためスキップ index=7 row=8
[3DPLVTX2XLS] 置換完了: changed vertices = 0
[3DPLVTX2XLS] コマンド終了
ログを見る限り、Excel 側のセルは空ではないのに、AutoLISP 側で “nil 扱い” になっているパターンです。
この症状の第一容疑はこれです:原因(ほぼこれ)
(vlax-get-property rng 'Value2)の戻り値が VARIANT になっていて、
そのままtype判定するとINT/REAL/STRに当たらず、結果的に nil扱いになっています。あなたが以前の書き込み側で
vlax-make-variantを使っていた流れとも整合します。
読む側もvlax-variant-valueで剥がす必要があります。
修正:Excelセル読み取りを「VARIANT対応」にする
下の2点だけ差し替えればOKです。
1) _3dplxls2vtx-read-cell を差し替え(VARIANTを剥がす)
(defun _3dplxls2vtx-read-cell (ws rowNum colNum / addr rng raw val) (setq addr (strcat (_3dplxls2vtx-col->name colNum) (itoa rowNum))) (setq rng (vlax-get-property ws 'Range addr)) (setq raw (vlax-get-property rng 'Value2)) (vlax-release-object rng) ;; ★重要:VARIANTなら中身を取り出す (cond ((null raw) nil) ((= (type raw) 'VARIANT) (vlax-variant-value raw)) (T raw) ) )
2) _3dplxls2vtx-read-num はそのままでOK(念のため STR→atof も追加推奨)
Excel が「文字列の数値」を返す場合に強くするなら、STR分岐をこうしておくと安定します(カンマ対応):
(defun _3dplxls2vtx-read-num (ws rowNum colNum / v s n) (setq v (_3dplxls2vtx-read-cell ws rowNum colNum)) (cond ((null v) nil) ((= (type v) 'INT) (float v)) ((= (type v) 'REAL) v) ((= (type v) 'STR) ;; "1,234.56" 対応 (setq s (vl-string-subst "" "," v)) (setq n (distof s 2)) (if n n (atof s)) ) (T nil) ) )
追加で効く「切り分けログ」(1回だけ出す)
もしまだ読めない場合に備えて、最初の頂点だけ raw の型と値をログ出しするのが早いです。
頂点ループの最初(index=1のとき)にこれを入れてください:(if (= vtxIdx 1) (_3dplxls2vtx-log (strcat "DEBUG A2 type=" (vl-princ-to-string (type (_3dplxls2vtx-read-cell xlSheet 2 1))) " val=" (vl-princ-to-string (_3dplxls2vtx-read-cell xlSheet 2 1)) ) ) )
もう1つの落とし穴(念のため)
今のコードは
Activate → ActiveSheet 取り直しをしていますが、読み取りは ActiveSheet を取り直さなくてもOKです。
(シートを検索して取得したxlSheetをそのまま使う方が “別ブックに切り替わった”事故を減らせます)もし不安なら、この2行は削除しても動きます:
(vlax-invoke-method xlSheet 'Activate) (vlax-release-object xlSheet) (setq xlSheet (vlax-get-property xlApp 'ActiveSheet))代わりに
ActivateだけにしてxlSheetは保持:(vlax-invoke-method xlSheet 'Activate) ;; xlSheet はそのまま使う(最後に
xlSheetを release すればOK)
次の返答で欲しいもの(最短で直すため)
上の VARIANT対応版に差し替えしたあとに、まだスキップされるなら、
- Excel の A2 の中身が「数値」か「文字列」か(見た目でOK)
- そして上の DEBUG ログ1行の結果
これだけで原因を確実に潰せます。
修正箇所を出してくれましたが、経験上コピペでの修正はあまりよろしくないので、コードをまるまる出し直してもらいます。
上記の修正を施した、完全版のコードを書いてください。
(vl-load-com)
;; ----------------------------
;; logger
;; ----------------------------
(defun _3dplxls2vtx-log (msg)
(princ (strcat "\n[3DPLXLS2VTX] " msg))
)
;; ----------------------------
;; 列番号→列名(1->A, 2->B, 27->AA ...)
;; ----------------------------
(defun _3dplxls2vtx-col->name (colNum / n r s)
(setq n colNum)
(setq s "")
(while (> n 0)
(setq r (rem (1- n) 26))
(setq s (strcat (chr (+ 65 r)) s))
(setq n (/ (- n r 1) 26))
)
s
)
;; ----------------------------
;; Excel セル読み取り(Range("A2"))
;; - Value2 の戻りが VARIANT の場合があるため剥がす
;; - Range は必ず release
;; ----------------------------
(defun _3dplxls2vtx-read-cell (ws rowNum colNum / addr rng raw val)
(setq addr (strcat (_3dplxls2vtx-col->name colNum) (itoa rowNum)))
(setq rng (vlax-get-property ws 'Range addr))
(setq raw (vlax-get-property rng 'Value2))
(vlax-release-object rng)
(cond
((null raw) nil)
((= (type raw) 'VARIANT) (vlax-variant-value raw))
(T raw)
)
)
;; ----------------------------
;; Excel値→数値(読めなければ nil)
;; - STR は distof / atof で解釈(カンマ除去も対応)
;; ----------------------------
(defun _3dplxls2vtx-read-num (ws rowNum colNum / v s n)
(setq v (_3dplxls2vtx-read-cell ws rowNum colNum))
(cond
((null v) nil)
((= (type v) 'INT) (float v))
((= (type v) 'REAL) v)
((= (type v) 'STR)
(setq s (vl-string-subst "" "," v))
(setq n (distof s 2))
(if n n (atof s))
)
(T nil)
)
)
;; ----------------------------
;; 起動中の Excel を取得(起動してなければ nil)
;; ----------------------------
(defun _3dplxls2vtx-get-running-excel ( / r)
(setq r (vl-catch-all-apply 'vlax-get-object (list "Excel.Application")))
(if (vl-catch-all-error-p r) nil r)
)
;; ----------------------------
;; ブック中からシート名一致を取得(無ければ nil)
;; - Worksheets/Sheet は必ず release(返すシート以外)
;; ----------------------------
(defun _3dplxls2vtx-get-sheet-by-name (wb targetName / wss cnt idx ws found)
(setq wss (vlax-get-property wb 'Worksheets))
(setq cnt (vlax-get-property wss 'Count))
(setq found nil)
(setq idx 1)
(while (and (<= idx cnt) (null found))
(setq ws (vlax-get-property wss 'Item idx))
(if (= (vlax-get-property ws 'Name) targetName)
(setq found ws)
(vlax-release-object ws)
)
(setq idx (1+ idx))
)
(vlax-release-object wss)
found
)
;; ----------------------------
;; VERTEX の 10座標を書き換え
;; ----------------------------
(defun _3dplxls2vtx-set-vertex-xyz (vtxEnt x y z / ed oldP newEd)
(setq ed (entget vtxEnt))
(setq oldP (assoc 10 ed))
(if (null oldP)
nil
(progn
(setq newEd (subst (cons 10 (list x y z)) oldP ed))
(entmod newEd)
(entupd vtxEnt)
T
)
)
)
(defun c:3DPLXLS2VTX
( /
entPick entName vlaObj objName
xlApp xlBook xlSheet sheetName
e ed etype vtxIdx rowNum
xVal yVal zVal ok changedCount
)
;; ============================
;; Main
;; ============================
(_3dplxls2vtx-log "コマンド開始")
;; Excel 起動確認(起動してなければ終了)
(setq xlApp (_3dplxls2vtx-get-running-excel))
(if (null xlApp)
(progn
(_3dplxls2vtx-log "Excel が起動していません。コマンド終了。")
(princ)
(exit)
)
)
(_3dplxls2vtx-log "Excel 起動中を確認")
;; ActiveWorkbook 取得(無ければ終了)
(setq xlBook (vlax-get-property xlApp 'ActiveWorkbook))
(if (null xlBook)
(progn
(_3dplxls2vtx-log "ActiveWorkbook が取得できません。コマンド終了。")
(princ)
(exit)
)
)
(_3dplxls2vtx-log "ActiveWorkbook を取得")
;; 対象シート取得(無ければ終了)
(setq sheetName "3DPL_頂点座標")
(setq xlSheet (_3dplxls2vtx-get-sheet-by-name xlBook sheetName))
(if (null xlSheet)
(progn
(_3dplxls2vtx-log (strcat "対象シートがありません: " sheetName " / コマンド終了。"))
(if xlBook (vlax-release-object xlBook))
(princ)
(exit)
)
)
(_3dplxls2vtx-log (strcat "対象シート取得: " sheetName))
;; ここは「取り直し事故」を避けるため、xlSheet を保持したまま Activate のみにする
(vlax-invoke-method xlSheet 'Activate)
;; 3Dポリライン選択
(setq entPick (entsel "\n3Dポリラインを1つ選択: "))
(if (null entPick)
(progn
(_3dplxls2vtx-log "選択がキャンセルされました。")
(if xlSheet (vlax-release-object xlSheet))
(if xlBook (vlax-release-object xlBook))
(princ)
(exit)
)
)
(setq entName (car entPick))
;; 3Dポリライン判定
(setq vlaObj (vlax-ename->vla-object entName))
(setq objName (vlax-get-property vlaObj 'ObjectName))
(_3dplxls2vtx-log (strcat "Selected ObjectName = " objName))
(vlax-release-object vlaObj)
(if (not (wcmatch (strcase objName) "*3DPOLYLINE*"))
(progn
(_3dplxls2vtx-log "3Dポリラインではありません。コマンド終了。")
(if xlSheet (vlax-release-object xlSheet))
(if xlBook (vlax-release-object xlBook))
(princ)
(exit)
)
)
;; 頂点走査しながら Excel を読む(1行目ヘッダ→2行目から)
(_3dplxls2vtx-log "Excel 2行目から A/B/C を読み、頂点座標を置換します")
(setq changedCount 0)
(setq vtxIdx 0)
(setq e (entnext entName))
(while e
(setq ed (entget e))
(setq etype (cdr (assoc 0 ed)))
(cond
((= etype "VERTEX")
(setq vtxIdx (1+ vtxIdx))
(setq rowNum (+ vtxIdx 1)) ;; 2行目から
(setq xVal (_3dplxls2vtx-read-num xlSheet rowNum 1))
(setq yVal (_3dplxls2vtx-read-num xlSheet rowNum 2))
(setq zVal (_3dplxls2vtx-read-num xlSheet rowNum 3))
;; デバッグ:最初の行だけ型と値を確認したい場合(必要ならコメント解除)
;; (if (= vtxIdx 1)
;; (_3dplxls2vtx-log
;; (strcat "DEBUG A2 type="
;; (vl-princ-to-string (type (_3dplxls2vtx-read-cell xlSheet 2 1)))
;; " val="
;; (vl-princ-to-string (_3dplxls2vtx-read-cell xlSheet 2 1))
;; )
;; )
;; )
(if (and xVal yVal zVal)
(progn
(setq ok (_3dplxls2vtx-set-vertex-xyz e xVal yVal zVal))
(if ok
(progn
(setq changedCount (1+ changedCount))
(if (= (rem vtxIdx 20) 0)
(_3dplxls2vtx-log (strcat " changed vertex " (itoa vtxIdx) " ..."))
)
)
(_3dplxls2vtx-log (strcat " 頂点更新失敗 index=" (itoa vtxIdx)))
)
)
(_3dplxls2vtx-log (strcat " 数値読取不可/空欄のためスキップ index=" (itoa vtxIdx) " row=" (itoa rowNum)))
)
)
((= etype "SEQEND")
(setq e nil)
)
)
(if e (setq e (entnext e)))
)
(_3dplxls2vtx-log (strcat "置換完了: changed vertices = " (itoa changedCount)))
;; 後始末(規約通り)
(if xlSheet (vlax-release-object xlSheet))
(if xlBook (vlax-release-object xlBook))
;; Excel.Application は原則 release しない
(_3dplxls2vtx-log "コマンド終了")
(princ)
)
試してみます。


OKです。
3Dポリラインの各頂点の座標値が、Excel から読み取った値に変更されています。
※ 黄色のラインは変更前の3Dポリラインの形状です。
まとめ
ChatGPTを使って、「3Dポリラインの頂点座標をエクセルの数値をもとに変更する」オリジナルコマンドを作成することが出来ました。
前回の「3Dポリラインの頂点座標を Excel に出力する」コマンドとあわせて使えば、Excel を3Dポリラインの作業パレットのように用いることができます。
また、ショートカットキーにこれらのコマンドを割り当てておくと、さらにストレスフリーで作業効率が上がると思います。


コメント