C#でAutoCADCHATBOT動作確認に成功!
の記事の続編。
F#はVB.net同様、手動でのパス通しをする羽目になった。
open Autodesk.AutoCAD.Runtime
open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
type MyCommands() =
interface IExtensionApplication with
member this.Initialize() =
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nMyCommands initialized")
member this.Terminate() =
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nMyCommands terminated")
[<CommandMethod("bot")>]
member this.ProcessUserInput() =
let doc = Application.DocumentManager.MdiActiveDocument
let ed = doc.Editor
ed.WriteMessage("\nProcessUserInput called")
let userInput = this.GetStringFromUserInput("CAD", "何をしましょうか?")
if userInput.Contains("直線") then
this.CreateLine()
elif userInput.Contains("円") then
this.CreateCircle()
else
ed.WriteMessage("\nそのような操作はできません。")
member private this.CreateLine() =
let doc = Application.DocumentManager.MdiActiveDocument
let ed = doc.Editor
let start_x = this.GetDoubleFromUserInput("始点のX座標を入力してください。")
let start_y = this.GetDoubleFromUserInput("始点のY座標を入力してください。")
let end_x = this.GetDoubleFromUserInput("終点のX座標を入力してください。")
let end_y = this.GetDoubleFromUserInput("終点のY座標を入力してください。")
this.DrawLine(start_x, start_y, end_x, end_y)
member private this.CreateCircle() =
let doc = Application.DocumentManager.MdiActiveDocument
let ed = doc.Editor
let center_x = this.GetDoubleFromUserInput("中心のX座標を入力してください。")
let center_y = this.GetDoubleFromUserInput("中心のY座標を入力してください。")
let radius = this.GetDoubleFromUserInput("半径を入力してください。")
this.DrawCircle(center_x, center_y, radius)
member private this.GetDoubleFromUserInput(prompt: string) : double =
let ed = Application.DocumentManager.MdiActiveDocument.Editor
let mutable result = ed.GetDouble(prompt)
while result.Status <> PromptStatus.OK do
ed.WriteMessage("\n数値を入力してください。")
result <- ed.GetDouble(prompt)
result.Value
member private this.GetStringFromUserInput(title: string, prompt: string) : string =
let ed = Application.DocumentManager.MdiActiveDocument.Editor
(ed.GetString(prompt)).StringResult
member private this.DrawLine(startX: double, startY: double, endX: double, endY: double) =
let doc = Application.DocumentManager.MdiActiveDocument
let db = doc.Database
use tr = db.TransactionManager.StartTransaction()
let bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
let btr = tr.GetObject(bt.[BlockTableRecord.ModelSpace], OpenMode.ForWrite) :?> BlockTableRecord
let startPoint = Point3d(startX, startY, 0.0)
let endPoint = Point3d(endX, endY, 0.0)
let line = new Line(startPoint, endPoint)
btr.AppendEntity(line) |> ignore
tr.AddNewlyCreatedDBObject(line, true) |> ignore
tr.Commit()
member private this.DrawCircle(centerX: double, centerY: double, radius: double) =
let doc = Application.DocumentManager.MdiActiveDocument
let db = doc.Database
use tr = db.TransactionManager.StartTransaction()
let bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
let btr = tr.GetObject(bt.[BlockTableRecord.ModelSpace], OpenMode.ForWrite) :?> BlockTableRecord
let centerPoint = Point3d(centerX, centerY, 0.0)
let circle = new Circle(centerPoint, Vector3d.ZAxis, radius)
btr.AppendEntity(circle) |> ignore
tr.AddNewlyCreatedDBObject(circle, true) |> ignore
tr.Commit()
ACETUCS-BUTTON
というコマンドが勝手に呼び出され、ロード中・・・と表示されたままフリーズ・
誰もACETUCS-BUTTONなんていうコマンドを入力した覚えがないんだけど。
F#は使えない言語ということでソッコー没!
次はC++でためしてみることになった。
どうなるかわからんが、とりあえずChatGPTの吐いたソースコードをここに投げておこう。
#include "acutads.h"
#include "acedads.h"
#include "acdocman.h"
#include "dbents.h"
#include "dbmain.h"
#include "dbsymtb.h"
#include "dbapserv.h"
#include "geassign.h"
#include "acarray.h"
class MyCommands
{
public:
static void ProcessUserInput()
{
AcDbDatabase* pDb = acdbHostApplicationServices()->workingDatabase();
AcDbBlockTable* pBlockTable;
AcDbBlockTableRecord* pBlockTableRecord;
acdbHostApplicationServices()->workingDatabase()->getBlockTable(pBlockTable, AcDb::kForRead);
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);
pBlockTable->close();
ACHAR szPrompt[512];
acutPrintf(_T("\nCAD、何をしましょうか?: "));
acedGetString(0, szPrompt);
CString userInput = szPrompt;
if (userInput.Find(_T("直線")) != -1)
{
CreateLine(pBlockTableRecord);
}
else if (userInput.Find(_T("円")) != -1)
{
CreateCircle(pBlockTableRecord);
}
else
{
acutPrintf(_T("\nそのような操作はできません。"));
}
pBlockTableRecord->close();
}
private:
static void CreateLine(AcDbBlockTableRecord* pBlockTableRecord)
{
double start_x = GetDoubleFromUserInput(_T("始点のX座標を入力してください。"));
double start_y = GetDoubleFromUserInput(_T("始点のY座標を入力してください。"));
double end_x = GetDoubleFromUserInput(_T("終点のX座標を入力してください。"));
double end_y = GetDoubleFromUserInput(_T("終点のY座標を入力してください。"));
AcDbLine* pLine = new AcDbLine(AcGePoint3d(start_x, start_y, 0), AcGePoint3d(end_x, end_y, 0));
pBlockTableRecord->appendAcDbEntity(pLine);
pLine->close();
}
static void CreateCircle(AcDbBlockTableRecord* pBlockTableRecord)
{
double center_x = GetDoubleFromUserInput(_T("中心のX座標を入力してください。"));
double center_y = GetDoubleFromUserInput(_T("中心のY座標を入力してください。"));
double radius = GetDoubleFromUserInput(_T("半径を入力してください。"));
AcDbCircle* pCircle = new AcDbCircle(AcGePoint3d(center_x, center_y, 0), AcGeVector3d::kZAxis, radius);
pBlockTableRecord->appendAcDbEntity(pCircle);
pCircle->close();
}
static double GetDoubleFromUserInput(const ACHAR* prompt)
{
double value;
ACHAR szPrompt[512];
acutPrintf(prompt);
acedInitGet(0, _T("Cancel"));
if (RTNORM != acedGetReal(szPrompt, &value))
{
acutPrintf(_T("\n数値を入力してください。"));
acutPrintf(_T("\n操作をキャンセルしました。"));
acedGetPoint(nullptr, _T("\n続行するにはリターン キーを押してください。"));
acutPrintf(_T("\n"));
return -1.0;
}
return value;
}
};
extern "C" AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
switch (msg)
{
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(pkt);
acrxRegisterAppMDIAware(pkt);
break;
case AcRx::kUnloadAppMsg:
// Do nothing
break;
}
return AcRx::kRetOK;
}
extern "C" void
ProcessUserInput()
{
MyCommands::ProcessUserInput();
}
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Threading;
namespace TetrisPlugin
{
public class TetrisSquare
{
public int X { get; set; }
public int Y { get; set; }
public string Color { get; set; }
public TetrisSquare(int x = 0, int y = 0, string color = "gray")
{
X = x;
Y = y;
Color = color;
}
public Point2d GetCord()
{
return new Point2d(X, Y);
}
public void SetColor(string color)
{
Color = color;
}
public string GetColor()
{
return Color;
}
public Point2d GetMovedCord(int direction)
{
int x = X;
int y = Y;
switch (direction)
{
case TetrisGame.MOVE_LEFT:
x--;
break;
case TetrisGame.MOVE_RIGHT:
x++;
break;
case TetrisGame.MOVE_DOWN:
y++;
break;
}
return new Point2d(x, y);
}
}
public class TetrisField
{
private readonly int width = 10; // Fix width and height
private readonly int height = 20;
private TetrisSquare[,] squares;
public TetrisField()
{
squares = new TetrisSquare[height, width];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
squares[y, x] = new TetrisSquare(x, y, "gray");
}
}
}
public int GetWidth()
{
return width;
}
public int GetHeight()
{
return height;
}
public TetrisSquare GetSquare(int x, int y)
{
return squares[y, x];
}
public bool JudgeGameOver(TetrisBlock block)
{
HashSet<Point2d> noEmptyCord = new HashSet<Point2d>();
HashSet<Point2d> blockCord = new HashSet<Point2d>();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (squares[y, x].GetColor() != "gray")
noEmptyCord.Add(new Point2d(x, y));
}
}
foreach (TetrisSquare square in block.GetSquares())
{
Point2d cord = square.GetCord();
blockCord.Add(cord);
}
blockCord.IntersectWith(noEmptyCord);
return blockCord.Count > 0;
}
public bool JudgeCanMove(TetrisBlock block, int direction)
{
HashSet<Point2d> noEmptyCord = new HashSet<Point2d>();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (squares[y, x].GetColor() != "gray")
noEmptyCord.Add(new Point2d(x, y));
}
}
HashSet<Point2d> moveBlockCord = new HashSet<Point2d>();
foreach (TetrisSquare square in block.GetSquares())
{
Point2d cord = square.GetMovedCord(direction);
moveBlockCord.Add(cord);
if (cord.X < 0 || cord.X >= width || cord.Y < 0 || cord.Y >= height)
return false;
}
moveBlockCord.IntersectWith(noEmptyCord);
return moveBlockCord.Count == 0;
}
public void FixBlock(TetrisBlock block)
{
foreach (TetrisSquare square in block.GetSquares())
{
int x = (int)square.GetCord().X;
int y = (int)square.GetCord().Y;
string color = square.GetColor();
squares[y, x].SetColor(color);
}
}
public void DeleteLine()
{
for (int y = height - 1; y >= 0; y--)
{
bool isFull = true;
for (int x = 0; x < width; x++)
{
if (squares[y, x].GetColor() == "gray")
{
isFull = false;
break;
}
}
if (isFull)
{
for (int yy = y; yy > 0; yy--)
{
for (int x = 0; x < width; x++)
{
squares[yy, x].SetColor(squares[yy - 1, x].GetColor());
}
}
for (int x = 0; x < width; x++)
{
squares[0, x].SetColor("gray");
}
y++;
}
}
}
}
public class TetrisBlock
{
private List<TetrisSquare> squares = new List<TetrisSquare>();
public TetrisBlock()
{
int blockType = new Random().Next(1, 5);
string color;
List<Point2d> cords = new List<Point2d>();
switch (blockType)
{
case 1:
color = "red";
cords.Add(new Point2d(10 / 2, 0));
cords.Add(new Point2d(10 / 2, 1));
cords.Add(new Point2d(10 / 2, 2));
cords.Add(new Point2d(10 / 2, 3));
break;
case 2:
color = "blue";
cords.Add(new Point2d(10 / 2, 0));
cords.Add(new Point2d(10 / 2, 1));
cords.Add(new Point2d(10 / 2 - 1, 0));
cords.Add(new Point2d(10 / 2 - 1, 1));
break;
case 3:
color = "green";
cords.Add(new Point2d(10 / 2 - 1, 0));
cords.Add(new Point2d(10 / 2, 0));
cords.Add(new Point2d(10 / 2, 1));
cords.Add(new Point2d(10 / 2, 2));
break;
case 4:
color = "orange";
cords.Add(new Point2d(10 / 2, 0));
cords.Add(new Point2d(10 / 2 - 1, 0));
cords.Add(new Point2d(10 / 2 - 1, 1));
cords.Add(new Point2d(10 / 2 - 1, 2));
break;
default:
color = "gray";
break;
}
foreach (Point2d cord in cords)
{
squares.Add(new TetrisSquare((int)cord.X, (int)cord.Y, color));
}
}
public List<TetrisSquare> GetSquares()
{
return squares;
}
public void Move(int direction)
{
foreach (TetrisSquare square in squares)
{
Point2d movedCord = square.GetMovedCord(direction);
square.X = (int)movedCord.X;
square.Y = (int)movedCord.Y;
}
}
}
public class TetrisGame
{
private TetrisField field;
private TetrisBlock block;
private TetrisCanvas canvas;
public const int MOVE_LEFT = 0;
public const int MOVE_RIGHT = 1;
public const int MOVE_DOWN = 2;
public TetrisGame(TetrisCanvas canvas, TetrisField field, TetrisBlock block)
{
this.canvas = canvas;
this.field = field;
this.block = block;
}
public void Start(Action endFunc)
{
field = new TetrisField();
block = null;
canvas.Update(field, block);
}
public void NewBlock()
{
block = new TetrisBlock();
if (field.JudgeGameOver(block))
{
Console.WriteLine("GAMEOVER");
}
canvas.Update(field, block);
}
public void MoveBlock(int direction)
{
if (field.JudgeCanMove(block, direction))
{
block.Move(direction);
canvas.Update(field, block);
}
else
{
if (direction == MOVE_DOWN)
{
field.FixBlock(block);
field.DeleteLine();
NewBlock();
}
}
}
}
public class TetrisCanvas
{
private TetrisField beforeField;
private TetrisField field;
private readonly int BLOCK_SIZE = 25;
private readonly int FIELD_WIDTH = 10;
private readonly int FIELD_HEIGHT = 20;
public TetrisCanvas(TetrisField field)
{
this.field = field;
beforeField = field;
}
public void Update(TetrisField field, TetrisBlock block)
{
beforeField = field;
// Update canvas with new field and block
}
}
public class TetrisCommands
{
[CommandMethod("TETRIS")]
public void TetrisGame()
{
TetrisField field = new TetrisField();
TetrisBlock block = null;
TetrisCanvas canvas = new TetrisCanvas(field);
TetrisGame game = new TetrisGame(canvas, field, block);
game.Start(null); // You can pass a function to handle end game scenario
while (true)
{
// Get user input, for example through AutoCAD commands or UI buttons
// Depending on input, call game.MoveBlock(direction) with appropriate direction
}
}
}
}
プログラムを実行してみたが、エラーは出なかった。しかしAutoCADでTETRISとコマンドを入れたらそれっきりウンともスンとも言わなくなってしまった。
前回はVisualBasic.netで成功させたから、今度はChatGPTにC#に変換してもらって試してみた。
※コマンド名に注目www
直線を書いてみた。
ソースコードの一部。
いわゆるパスを通すという作業の様子。
ここが躓いた原因。VB.NETの時はXMLタグを編集したがC#ではプロパティで開いて入力ボックスにacad.exeのディレクトリをフルパスで入力した。
いわゆる参照という作業の様子。
これはVB.NETの時と一緒。
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
public class MyCommands
{
[CommandMethod("両足チョンパの崔バカ雄")]
public void ProcessUserInput()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
string userInput = GetStringFromUserInput("CAD", "何をしましょうか?");
if (userInput.Contains("直線"))
{
CreateLine();
}
else if (userInput.Contains("円"))
{
CreateCircle();
}
else
{
ed.WriteMessage("そのような操作はできません。");
}
}
private void CreateLine()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
double start_x = GetDoubleFromUserInput("始点のX座標を入力してください。");
double start_y = GetDoubleFromUserInput("始点のY座標を入力してください。");
double end_x = GetDoubleFromUserInput("終点のX座標を入力してください。");
double end_y = GetDoubleFromUserInput("終点のY座標を入力してください。");
DrawLine(start_x, start_y, end_x, end_y);
}
private void CreateCircle()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
double center_x = GetDoubleFromUserInput("中心のX座標を入力してください。");
double center_y = GetDoubleFromUserInput("中心のY座標を入力してください。");
double radius = GetDoubleFromUserInput("半径を入力してください。");
DrawCircle(center_x, center_y, radius);
}
private double GetDoubleFromUserInput(string prompt)
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
while (true)
{
PromptDoubleResult result = ed.GetDouble(prompt);
if (result.Status == PromptStatus.OK)
{
return result.Value;
}
else
{
ed.WriteMessage("数値を入力してください。");
}
}
}
private string GetStringFromUserInput(string title, string prompt)
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
return ed.GetString(prompt).StringResult;
}
private void DrawLine(double startX, double startY, double endX, double endY)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
Point3d startPoint = new Point3d(startX, startY, 0);
Point3d endPoint = new Point3d(endX, endY, 0);
Line line = new Line(startPoint, endPoint);
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
tr.Commit();
}
}
private void DrawCircle(double centerX, double centerY, double radius)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
Point3d centerPoint = new Point3d(centerX, centerY, 0);
Circle circle = new Circle(centerPoint, Vector3d.ZAxis, radius);
btr.AppendEntity(circle);
tr.AddNewlyCreatedDBObject(circle, true);
tr.Commit();
}
}
}
AutoCAD VB.NETマクロサンプル大全集に書かれてある通りにC#でもやればいいのではないかと思ったが、失敗してしまった。
以前購入したAmazon kindleの英語で書かれたe-bookを見ながらやってみたら、三度目の正直で成功!
VB.NETでは押出くり抜きとかもっと高度なことをやろうとしてChatGPTにコーディングしてもらったが、如何せんエラーが直らず断念。
C#ではもっと高度なことができると期待している。とりあえずVB.NETはこれにて終了。
Microsoft、Visual Studio Code上にC#開発環境を構築できる拡張機能「C# Dev Kit」を発表
https://codezine.jp/article/detail/17892
https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit
これからVisual C#を始めようとしていてVisual Studio Communityのインストールで悩んでいる者に朗報?
特にCドライブがいっぱいでインストールできない者にとって有効か?
俺もその一人。AutoCADとかごついアプリばかり入っていてCドライブの容量を圧迫されている場合、深刻な問題だ。
どうあがいてもCドライブにインストールされてしまうので、VSCodeしか入れられなかった。
Visual Studio Code上で開発環境を構築できるらしい。