「...彼らは何が起こるか知りたい」か、C#(MultiCAD .NET API)でCAD NanoCADに占いボールを書く

ソビエト映画の古いを信じるなら、人々は常に困難な状況で未来の質問に興味を持っています。 誰かがコインを投げ、誰かがポールのタコを拷問し、絶対に凶悪な人々がカモミールを摘み取ります。 私たちはもっと人道的で、非常に型にはまらないアプリケーションであるCAD用NanoCADを見つけます。



この記事では、MultiCAD.NET APIを使用してカスタムNanoCADプリミティブを作成するためのトレーニングを再度行い、Windows.Formsとの相互作用をオブジェクトにねじ込みます。



今日のコードはC#のみで、有料版(NC 8.5)および無料(NC 5.1)向けに記述します。当然、LinuxユーザーはMonoでコンパイルしてWineで実行できるため、大歓迎です...











このミニサイクルの記事に出会ったことがない場合は、ネタバレを見ることもできます。 以前の記事では、NanoCADがLinuxでプロジェクトを起動しようとすることから生じるさまざまな問題を取り上げました。





いつものように、私はプログラマーではないため、この記事の私の考えすべて正しいとは限らないこと、またNanoCAD開発者とは関わりがないこと思い出します。 NanoCADのユーザーと開発者のコ​​ミュニティ全体にフォーラムでの支援に感謝する必要があると私は確信していますが。



設計とは関係のないオブジェクトを作成するために再びCADソフトウェアを使用することに驚かないでください。 単純に、Windows.FormsをNanoCADユーザーオブジェクトに「埋め込む」ことを練習する必要がありました。NanocadのAPIのトレーニング資料が「泣いている」ため、簡単で明確な例を共有することにしました。



この記事は短く、内容は省略できますが、念のため、ナビゲーションを簡単にするためにそのままにしておきます。



内容:

パートI:はじめに

パートII:C#コードの記述

パートIII:結論



トレーニング資料が私たちが望むよりも少ないという事実にもかかわらず、コードを書くときに頼りになるものがあると言わなければなりません。



特に、開発者はHabréのブログでユーザープリミティブの作成について既に書いています。 まあ 、インテリジェントペンの問題も以前に対処されていました 。 原則として、今日はこれら2つの記事の範囲をはるかに超えることはありません。



通常、記事の冒頭でNanoCADのプロジェクトをゼロから作成する方法を書いていますが、今回はウィンドウフォームを持つクラスが存在するため、プロジェクト全体をGitHubに配置することを決定しました。



ただし、Nanocadの開発が初めての場合は、以前の記事のこの部分をご覧ください( NC 8.5およびNC 5.1の場合 )。



念のため、NanoCAD開発者を「怒らせない」ことにし、必要なライブラリをSDKからプロジェクトに適用しませんでした。 これらのライブラリは、インストールされたプログラムの「bin」フォルダーにあるか、SDKを受信することで見つけることができます。 NC 8.5およびその他のバージョンについては、開発クラブに登録する必要があります。 念のため、クラブのメンバーは、開発目的のためにNCの利用可能なバージョンを無料でダウンロードできることを思い出してください。 さて、無料のNC 5.1の場合、SDKはプログラムにバンドルされているようです(何も変更されていない場合)。



そのため、コードの解析を開始します。フォームの自動生成されたコードは適用しませんが、ユーザープリミティブのクラス(実際にはボール自体)とフォームロジックに制限します。



最初に、スポイラーの下でNanoCADの有料版の完全なコードを非表示にしましょう。



C#のNC 8.5の完全なコード#
// Version 1.0 //Use Microsoft .NET Framework 4 and MultiCad.NET API 7.0 //Class for demonstrating the capabilities of MultiCad.NET //Assembly for the Nanocad 8.5 SDK is recommended (however, it is may be possible in the all 8. family) //Link imapimgd, mapimgd.dll and mapibasetypes.dll from SDK //Link System.Windows.Forms and System.Drawing //The commands: draws a fortune-teller ball //This code in the part of non-infringing rights Nanosoft can be used and distributed in any accessible ways. //For the consequences of the code application, the developer is not responsible. //More detailed - https://habrahabr.ru/post/347720/ using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; using Multicad.AplicationServices; namespace Fortuneteller { [CustomEntity(typeof(Ball), "2e814ea6-f1f0-469d-9767-269fedb32226", "Ball", "Fortuneteller Ball for NC85 Entity")] [Serializable] public class Ball : McCustomBase { private Point3d _basePnt = new Point3d(0, 0, 0); double _radius=300; string _predText = "..."; public List<String> predictions = new List<String>() {"Act now!", "Do not do this!", "Maybe", "I dont know", "Everything is unclear", "Yes!", "No!", "Take rest" }; public override void OnDraw(GeometryBuilder dc) { dc.Clear(); dc.Color = McDbEntity.ByObject; dc.DrawCircle(_basePnt, _radius); dc.DrawCircle(_basePnt, _radius/2.0); dc.TextHeight = 31; dc.DrawMText(_basePnt, Vector3d.XAxis, _predText, HorizTextAlign.Center, VertTextAlign.Center, _radius / 2.05); } public override void OnTransform(Matrix3d tfm) { // To be able to cancel(Undo) McUndoPoint undo = new McUndoPoint(); undo.Start(); // Get the coordinates of the base point and the rotation vector this.TryModify(); this._basePnt = this._basePnt.TransformBy(tfm); undo.Stop(); } public override hresult OnEdit(Point3d pnt, EditFlags lInsertType) { CallForm(); return hresult.s_Ok; } private void CallForm() { ListEditorForm frm = new ListEditorForm(this); frm.Lpredictions.Items.AddRange(predictions.ToArray()); frm.ShowDialog(); } [CommandMethod("DFTBall", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawBall () { Ball ball = new Ball(); ball.PlaceObject(); McContext.ShowNotification("Use green grip or shake (move) ball to get prediction"); } public override bool GetGripPoints(GripPointsInfo info) { //frist grip to move info.AppendGrip(new McSmartGrip<Ball>(_basePnt+new Vector3d(0, _radius,0), (obj, g, offset) => { obj.TryModify(); obj._basePnt += offset; obj.TryModify(); obj.ShakePredict(); })); //command grip var ctxGrip = new McSmartGrip<Ball>(McBaseGrip.GripType.PopupMenu, 2, _basePnt - 1.0 * new Vector3d(_radius, 0, 0), McBaseGrip.GripAppearance.PopupMenu, 0, "Select menu", Color.Lime); ctxGrip.GetContextMenu = (obj, items) => { items.Add(new ContextMenuItem("Get prediction", "none", 1)); items.Add(new ContextMenuItem("Edit predictions", "none", 2)); }; ctxGrip.OnCommand = (obj, commandId, grip) => { if (grip.Id == 2) { switch (commandId) { case 1: { ShakePredict(); break; } case 2: { CallForm(); break; } } } }; info.AppendGrip(ctxGrip); return true; } public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select center point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _basePnt = res.Point; // Add the object to the database DbEntity.AddToCurrentDocument(); return hresult.s_Ok; } private void ShakePredict() { Random rand = new Random(); int val = rand.Next(0, predictions.Count); this.TryModify(); _predText = predictions[val]; } } }
      
      







ここで、パーツのキーポイントを分析します。



 using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; using Multicad.AplicationServices; namespace Fortuneteller { [CustomEntity(typeof(Ball), "2e814ea6-f1f0-469d-9767-269fedb32226", "Ball", "Fortuneteller Ball for NC85 Entity")] [Serializable] public class Ball : McCustomBase {
      
      





名前空間を接続し、ユーザーオブジェクトのクラスを作成し、ランダムに生成されたGUIDを割り当て、McCustomBaseからクラスを継承します。



  private Point3d _basePnt = new Point3d(0, 0, 0); double _radius=300; string _predText = "..."; public List<String> predictions = new List<String>() {"Act now!", "Do not do this!", "Maybe", "I dont know", "Everything is unclear", "Yes!", "No!", "Take rest" };
      
      





占いボールの基本変数を設定します。ジオメトリの中心点、半径、予測ウィンドウ内のテキスト、予測オプションのリストです。



  public override void OnDraw(GeometryBuilder dc) { dc.Clear(); dc.Color = McDbEntity.ByObject; dc.DrawCircle(_basePnt, _radius); dc.DrawCircle(_basePnt, _radius/2.0); dc.TextHeight = 31; dc.DrawMText(_basePnt, Vector3d.XAxis, _predText, HorizTextAlign.Center, VertTextAlign.Center, _radius / 2.05); }
      
      





メソッドはオブジェクトのレンダリングを担当します。 2つの円と複数行のテキストオブジェクトを描画します。



  public override void OnTransform(Matrix3d tfm) { // To be able to cancel(Undo) McUndoPoint undo = new McUndoPoint(); undo.Start(); // Get the coordinates of the base point and the rotation vector this.TryModify(); this._basePnt = this._basePnt.TransformBy(tfm); undo.Stop(); }
      
      





このメソッドは、オブジェクトが変更されたときに呼び出されます。どのように機能するかは100%わかりませんが、オブジェクトを正しく移動するために必要です。



 public override hresult OnEdit(Point3d pnt, EditFlags lInsertType) { CallForm(); return hresult.s_Ok; }
      
      





このメソッドは、ボールをダブルクリックした時点でフォームを呼び出します(記事の最後の図を参照)。



  private void CallForm() { ListEditorForm frm = new ListEditorForm(this); frm.Lpredictions.Items.AddRange(predictions.ToArray()); frm.ShowDialog(); }
      
      





フォームを直接呼び出します。 ボールに表示される予測のバリエーションを追加または削除するには、フォームが必要です。



ListEditorFormフォームクラスを事前に作成し、必要に応じて、ボールへのリンク(フィードバックが必要)を渡すことでオブジェクトを作成します。フォームを呼び出す前に、現在の予測リストをListBoxに入力します。



  [CommandMethod("DFTBall", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawBall() { Ball ball = new Ball(); ball.PlaceObject(); McContext.ShowNotification("Use green grip or shake (move) ball to get prediction"); }
      
      





占いボールを作成するチーム。

最も単純なケースでは、NanoCADコンソールでDFTBallを入力する必要があり、DrawBallメソッドを呼び出します(必要に応じて、Netloadコマンドでライブラリをロードすることを忘れないでください)。



 public override bool GetGripPoints(GripPointsInfo info) { //first grip to move info.AppendGrip(new McSmartGrip<Ball>(_basePnt+new Vector3d(0, _radius,0), (obj, g, offset) => { obj.TryModify(); obj._basePnt += offset; obj.TryModify(); obj.ShakePredict(); })); //command grip var ctxGrip = new McSmartGrip<Ball>(McBaseGrip.GripType.PopupMenu, 2, _basePnt - 1.0 * new Vector3d(_radius, 0, 0), McBaseGrip.GripAppearance.PopupMenu, 0, "Select menu", Color.Lime); ctxGrip.GetContextMenu = (obj, items) => { items.Add(new ContextMenuItem("Get prediction", "none", 1)); items.Add(new ContextMenuItem("Edit predictions", "none", 2)); }; ctxGrip.OnCommand = (obj, commandId, grip) => { if (grip.Id == 2) { switch (commandId) { case 1: { ShakePredict(); break; } case 2: { CallForm(); break; } } } }; info.AppendGrip(ctxGrip); return true; }
      
      





ここで、オブジェクトのハンドル(青と緑)を設定します。



まず、オブジェクトを移動するには青いハンドルが必要です。 青いハンドルでボールをドラッグすると、振ることができ、予測の線がどのように変化するかがわかります。



2つ目は緑色のハンドル(セクション//コマンドグリップ)です。2つのコマンドを含むウィンドウを表示する必要があります。 最初は新しい予測を生成し、2番目は予測リストエディターを呼び出します。



  public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select center point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _basePnt = res.Point; // Add the object to the database DbEntity.AddToCurrentDocument(); return hresult.s_Ok; }
      
      





このコードは、モデル空間にオブジェクトを配置するために呼び出されます。 まず、InputJigオブジェクトを作成し、それを介して挿入ポイントを要求し、ボールの幾何学的中心のポイントの座標を変更し、オブジェクトをドキュメントに追加します。



  private void ShakePredict() { Random rand = new Random(); int val = rand.Next(0, predictions.Count); this.TryModify(); _predText = predictions[val]; }
      
      





さて、ここでは、最も単純な乱数ジェネレーターを使用して、一般的なリストから何らかの予測を返します。



フォームのロジック、ネタバレの下に隠す完全なコードを詳細に分析しません。



クラスコードListEditorForm
 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Fortuneteller { public partial class ListEditorForm : Form { private Ball ball; public ListEditorForm() { InitializeComponent(); } public ListEditorForm(Ball ball) { this.ball = ball; InitializeComponent(); } private void listView1_SelectedIndexChanged(object sender, EventArgs e) { } private void DelBtn_Click(object sender, EventArgs e) { if (Lpredictions.SelectedItem !=null) { Lpredictions.Items.Remove(Lpredictions.SelectedItem); } } private void AdBtn_Click(object sender, EventArgs e) { if (textBox.Text!="" | textBox.Text != " ") { Lpredictions.Items.Add(textBox.Text); } } private void SaceBtn_Click(object sender, EventArgs e) { ball.predictions = Lpredictions.Items.OfType<String>().ToList(); this.Close(); } } }
      
      







おそらく、NanoCADと何らかの形で接続されているのは、保存と閉じるボタンのイベントハンドラーだけでしょう。



  private void SaceBtn_Click(object sender, EventArgs e) { ball.predictions = Lpredictions.Items.OfType<String>().ToList(); this.Close(); }
      
      





以前、ボールフォームへのリンクを送信していたことを覚えていますか? 次に、予測ボックスにListBoxのすべての値を書き留めてフォームを閉じます。その後、ボールは更新された予測の生成を開始します。 「十字」をクリックしてフォームを閉じると、結果は保存されません。



古い-無料のNanocadのコードはそれほど変わりません。



NanoCAD 5.1のコード
 // Version 1.0 //Use Microsoft .NET Framework 3.5 and MultiCad.NET API //Class for demonstrating the capabilities of MultiCad.NET //Assembly for the Nanocad 5.1 SDK is recommended //Link mapimgd.dll and hostmgd.dll from SDK //Link System.Windows.Forms and System.Drawing //The commands: draws a fortune-teller ball //This code in the part of non-infringing rights Nanosoft can be used and distributed in any accessible ways. //For the consequences of the code application, the developer is not responsible. //More detailed - https://habrahabr.ru/post/347720/ using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; using HostMgd.ApplicationServices; using HostMgd.EditorInput; namespace Fortuneteller { [CustomEntity(typeof(Ball), "2e814ea6-f1f0-469d-9767-269fedb32195", "Ball", "Fortuneteller Ball for NC51 Entity")] [Serializable] public class Ball : McCustomBase { private Point3d _basePnt = new Point3d(0, 0, 0); double _radius=300; string _predText = "..."; public List<String> predictions = new List<String>() {"Act now!", "Do not do this!", "Maybe", "I dont know", "Everything is unclear", "Yes!", "No!", "Take rest" }; public override void OnDraw(GeometryBuilder dc) { dc.Clear(); dc.Color = McDbEntity.ByObject; dc.DrawCircle(_basePnt, _radius); dc.DrawCircle(_basePnt, _radius/2.0); dc.TextHeight = 31; dc.DrawMText(_basePnt, Vector3d.XAxis, _predText, HorizTextAlign.Center, VertTextAlign.Center, _radius / 2.05); } public override void OnTransform(Matrix3d tfm) { // To be able to cancel(Undo) McUndoPoint undo = new McUndoPoint(); undo.Start(); // Get the coordinates of the base point and the rotation vector this.TryModify(); this._basePnt = this._basePnt.TransformBy(tfm); undo.Stop(); } public override hresult OnEdit(Point3d pnt, EditFlags lInsertType) { CallForm(); return hresult.s_Ok; } private void CallForm() { ListEditorForm frm = new ListEditorForm(this); frm.Lpredictions.Items.AddRange(predictions.ToArray()); frm.ShowDialog(); } [CommandMethod("DFTBall", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawBall() { Ball ball = new Ball(); ball.PlaceObject(); DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; ed.WriteMessage("Use green grip or shake (move) ball to get prediction"); } public override bool GetGripPoints(GripPointsInfo info) { //frist grip to move info.AppendGrip(new McSmartGrip<Ball>(_basePnt+new Vector3d(0, _radius,0), (obj, g, offset) => { obj.TryModify(); obj._basePnt += offset; obj.TryModify(); obj.ShakePredict(); })); //command grip var ctxGrip = new McSmartGrip<Ball>(McBaseGrip.GripType.PopupMenu, 2, _basePnt - 1.0 * new Vector3d(_radius, 0, 0), McBaseGrip.GripAppearance.PopupMenu, 0, "Select menu", Color.Lime); ctxGrip.GetContextMenu = (obj, items) => { items.Add(new ContextMenuItem("Get prediction", "none", 1)); items.Add(new ContextMenuItem("Edit predictions", "none", 2)); }; ctxGrip.OnCommand = (obj, commandId, grip) => { if (grip.Id == 2) { switch (commandId) { case 1: { ShakePredict(); break; } case 2: { CallForm(); break; } } } }; info.AppendGrip(ctxGrip); return true; } public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select center point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _basePnt = res.Point; // Add the object to the database DbEntity.AddToCurrentDocument(); return hresult.s_Ok; } private void ShakePredict() { Random rand = new Random(); int val = rand.Next(0, predictions.Count); this.TryModify(); _predText = predictions[val]; } } }
      
      







本質の違いはすべて次の点にあります。McContext.ShowNotification(「緑色のグリップまたはシェイク(移動)ボールを使用して予測を取得する」)がMultiCAD.NET APIの古いバージョンではまだ実装されていないため、シンプルな.NET API。



  DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; ed.WriteMessage("Use green grip or shake (move) ball to get prediction");
      
      







結局、青いハンドルまたは緑のハンドルに隠されたコマンドでドラッグすると、予測できるボールができました。



また、予測のリストを含む変数の編集を実装することにより、グラフィックフォームとオブジェクトの相互作用の最も単純な例を調べました。 フォームを呼び出すには、オブジェクトをダブルクリックするか、緑色のハンドルを使用して呼び出します。

あなたが手に入れたものはこちら



Nanocad 8.5







Nanocad 5.1無料







この例がコミックであり、実用的ではないことは明らかですが、それでも誰かに役立つことを願っています。



みなさん、良い一日を!



PS万が一に備えて、最新のWindows 10アップデートによりx64バージョンのNanoCAD 8が少し破損するため、すべてのコードがx86バージョンでテストされたことを警告します。



All Articles