Если верить одной старой песне из советского кинофильма, то люди всегда интересуются вопросами будущего в трудной ситуации. Кто-то подбрасывает монетку, кто-то мучает осьминога Пауля, а совсем уж зверски настроенные люди — ощипывают ромашки. Мы с вами поступим куда как гуманней и найдем для САПР NanoCAD, весьма нетрадиционное применение, а именно сделаем свой аналог гадального шара (почти как на картинке ниже).
В статье мы еще раз потренируемся создавать пользовательские примитивы NanoCAD с помощью MultiCAD.NET API, а также прикрутим к нашему объекту взаимодействие с Windows.Forms.
Код сегодня будет только на C#, писать его будем для платной версии (NC 8.5) и для бесплатной (NC 5.1), ну и естественно пользователи Linux смогут его собрать в Mono и запустить под Wine, поэтому милости прошу под кат…

Если вы раньше не сталкивались, со статьями из этого мини-цикла, то можно заглянуть под спойлер. В прошлых статьях мы рассмотрели разные вопросы от того, что такое NanoCAD, до попыток запустить наши проекты под Linux.
Как всегда, напомню, что я не программист и поэтому не все мои мысли в данной статье могут быть корректными, а также что с разработчиками NanoCAD я никак не ангажирован . Хотя безусловно считаю нужным — сказать спасибо всему сообществу пользователей и разработчиков NanoCAD за их помощь на форуме.
Пусть вас не удивляет, что мы опять будем использовать САПР для создания объектов, никак не связанных с проектированием. Просто, мне надо было потренироваться «вставлять» Windows.Forms в пользовательские объекты NanoCAD, а поскольку учебных материалов по API для Нанокада – «кот наплакал», то я решил с вами поделиться своим простым и наглядным примером.
Статья будет короткая и можно было бы обойтись без содержания, но я на всякий случай его для удобства навигации оставлю.
Содержание:
Часть I: введение
Часть II: пишем код на C#
Часть III: заключение
Надо сказать, что несмотря, на то, что учебных материалов меньше чем хотелось бы, при написании кода нам будет на что опираться.
В частности, про создание пользовательского примитива уже писали разработчики в своем блоге на Хабре, ну и вопрос с интеллектуальными ручками, тоже ранее ими разбирался. В принципе мы сегодня сильно за рамки этих двух статей не выйдем.
Обычно в начале статьи я пишу, как создать с нуля проект для NanoCAD, но в этот раз из-за наличия класса с оконной формой я решил выложить весь проект на GitHub, поэтому его можно просто скачать, подключить библиотеки и сразу начать экспериментировать.
Но если для вас разработка под Нанокад – в новинку, посмотрите вот этот кусочек прошлой статьи (для NC 8.5 и для NC 5.1 ).
Я решил на всякий случай не «обижать» разработчиков NanoCAD и не стал прикладывать к проекту необходимые библиотеки из пакета SDK. Данные библиотеки вы сможете найти либо в папке “bin”, установленной программы, либо получив пакет SDK. Для NC 8.5 и других версий необходимо зарегистрировать в клубе разработчиков. На всякий случай напомню, что скачать любую доступную версию NC для целей разработки, участники клуба могут совершенно бесплатно. Ну а для бесплатного NC 5.1 — SDK вроде бы поставляется в комплекте с программой (если ничего не поменялось).
Итак, начнем разбирать код, я не буду прикладывать автоматически созданный код формы, а ограничусь только классом пользовательского примитива (собственно сам шар) и логикой формы.
Для начала спрячем под спойлером полный код для платной версии NanoCAD.
Теперь разберем ключевые моменты по частям.
Подключаем пространства имен, создаем класс пользовательского объекта, присвоим ему какой-нибудь случайно сгенерированный GUID, наследуем наш класс от McCustomBase.
Задаём основные переменные для нашего гадального шара: точку центра геометрии, радиус, текст в окошке предсказания и список вариантов предсказания.
Метод отвечает за отрисовку объекта. Чертим два круга и объект многострочного текста.
Метод вызывается при изменении объекта, не на 100% понимаю, как он работает, но он будет нужен для того, чтобы корректно перемещать объект.
Метод будет вызывать нашу форму (см. картинку в конце статьи) в момент, когда мы сделаем двойной клик по шару.
Непосредственно вызов формы. Форма нам нужна для того, чтобы добавить или удалить варианты предсказаний, которые выпадают в шаре.
Мы заранее создали класс формы ListEditorForm и теперь при необходимости создаем объект, передав ему ссылку на наш шар (нужно для обратной связи), перед тем как вызвать форму заполняем её ListBox текущим списком предсказаний.
Команда, с помощью которой мы и будем создавать наш гадальный шар.
В самом простом случае в консоли NanoCAD надо будет ввести DFTBall и он вызовет наш метод DrawBall (не забудьте при необходимости загрузить библиотеку командой Netload).
Здесь мы задаем ручки объекта — синюю и зеленую.
Первая — синяя ручка нужна для перемещения объекта. Перетаскивая шар за синюю ручку его можно потрясти и вы увидите, как меняется строка предсказаний.
Вторая — зеленая ручка (секция //command grip), нужна нам для того, чтобы вывести окошко с двумя командами. Первая генерирует новое предсказание, вторая вызывает редактор списка предсказаний.
Этот код вызывается для размещения объекта в пространстве модели. Вначале мы создаем объект InputJig, через него запрашиваем точку вставки, изменяем координаты нашей точки геометрического центра шара и добавляем объект в документ.
Ну а тут с помощью простейшего генератора случайных чисел мы возвращаем какое-нибудь предсказание из общего списка.
Мы не будем разбирать подробно логику работы формы, полный код я спрячу под спойлер
Пожалуй, единственное, что хоть как-то тут связанно с NanoCAD это обработчик события кнопки «сохранить и закрыть».
Помните мы раньше передавали форме ссылку на наш шар? Теперь мы, обращаясь к нему записываем в список предсказаний все значения нашего ListBox и закрываем форму, после этого шар начнет выдавать обновленные предсказания. Если форму закрыть, нажав на «крестик», то результат не сохранится.
Код для старого — бесплатного Нанокада, сильно отличатся не будет.
Вся разница по сути только в следующем: из-за того, что McContext.ShowNotification(«Use green grip or shake (move) ball to get prediction») еще не реализован в старой версии MultiCAD.NET API, мы его заменили на аналог из простого .NET API.
Итак, в итоге мы получили шар, который может выдавать предсказания, если его перетаскивать за синюю ручку или по команде спрятанной в зеленой ручке.
Также мы рассмотрели, простейший пример взаимодействия графической формы и объекта, реализовав редактирование переменной содержащей список предсказаний. Напомню, что вызов формы производится по двойному щелчку на объект или через зеленую ручку.
Вот что получили в итоге
Nanocad 8.5

Nanocad 5.1 Free

Понятно, что данный пример — шуточный и практической пользы не имеет, но надеюсь, что он все же кому-нибудь пригодится.
Всем удачного дня!
P.S. На всякий случай предупрежу, что последнее обновление Windows 10 немного ломает x64 версию NanoCAD 8, поэтому весь код тестировался в x86 версиях.
В статье мы еще раз потренируемся создавать пользовательские примитивы NanoCAD с помощью MultiCAD.NET API, а также прикрутим к нашему объекту взаимодействие с Windows.Forms.
Код сегодня будет только на C#, писать его будем для платной версии (NC 8.5) и для бесплатной (NC 5.1), ну и естественно пользователи Linux смогут его собрать в Mono и запустить под Wine, поэтому милости прошу под кат…

Если вы раньше не сталкивались, со статьями из этого мини-цикла, то можно заглянуть под спойлер. В прошлых статьях мы рассмотрели разные вопросы от того, что такое NanoCAD, до попыток запустить наши проекты под Linux.
Другие статьи цикла
- «Лицо без шрама» или первые шаги в Multicad.NET API 7 (для Nanocad 8.1)
- «Как баран на новые ворота» или пользовательские «псевдо-3D» объекты в NanoCAD с помощью MultiCAD.NET API
- «Я слежу за тобой» или как из CADa сделать SCADA (MultiCAD.NET API)
- «Истина в вине» или пробуем программировать NanoCAD под Linux (MultiCAD.NET API)
- «Здравствуй елка — Новый Год!» или программируем NanoCAD с помощью Visual Basic .NET
Как всегда, напомню, что я не программист и поэтому не все мои мысли в данной статье могут быть корректными, а также что с разработчиками NanoCAD я никак не ангажирован . Хотя безусловно считаю нужным — сказать спасибо всему сообществу пользователей и разработчиков NanoCAD за их помощь на форуме.
Пусть вас не удивляет, что мы опять будем использовать САПР для создания объектов, никак не связанных с проектированием. Просто, мне надо было потренироваться «вставлять» Windows.Forms в пользовательские объекты NanoCAD, а поскольку учебных материалов по API для Нанокада – «кот наплакал», то я решил с вами поделиться своим простым и наглядным примером.
Статья будет короткая и можно было бы обойтись без содержания, но я на всякий случай его для удобства навигации оставлю.
Содержание:
Часть I: введение
Часть II: пишем код на C#
Часть III: заключение
Надо сказать, что несмотря, на то, что учебных материалов меньше чем хотелось бы, при написании кода нам будет на что опираться.
В частности, про создание пользовательского примитива уже писали разработчики в своем блоге на Хабре, ну и вопрос с интеллектуальными ручками, тоже ранее ими разбирался. В принципе мы сегодня сильно за рамки этих двух статей не выйдем.
Обычно в начале статьи я пишу, как создать с нуля проект для NanoCAD, но в этот раз из-за наличия класса с оконной формой я решил выложить весь проект на GitHub, поэтому его можно просто скачать, подключить библиотеки и сразу начать экспериментировать.
Но если для вас разработка под Нанокад – в новинку, посмотрите вот этот кусочек прошлой статьи (для NC 8.5 и для NC 5.1 ).
Я решил на всякий случай не «обижать» разработчиков NanoCAD и не стал прикладывать к проекту необходимые библиотеки из пакета SDK. Данные библиотеки вы сможете найти либо в папке “bin”, установленной программы, либо получив пакет SDK. Для NC 8.5 и других версий необходимо зарегистрировать в клубе разработчиков. На всякий случай напомню, что скачать любую доступную версию NC для целей разработки, участники клуба могут совершенно бесплатно. Ну а для бесплатного NC 5.1 — SDK вроде бы поставляется в комплекте с программой (если ничего не поменялось).
Итак, начнем разбирать код, я не буду прикладывать автоматически созданный код формы, а ограничусь только классом пользовательского примитива (собственно сам шар) и логикой формы.
Для начала спрячем под спойлером полный код для платной версии NanoCAD.
Полный код для NC 8.5 на C#
// 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); }
Метод отвечает за отрисовку объекта. Чертим два круга и объект многострочного текста.
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; }
Здесь мы задаем ручки объекта — синюю и зеленую.
Первая — синяя ручка нужна для перемещения объекта. Перетаскивая шар за синюю ручку его можно потрясти и вы увидите, как меняется строка предсказаний.
Вторая — зеленая ручка (секция //command grip), нужна нам для того, чтобы вывести окошко с двумя командами. Первая генерирует новое предсказание, вторая вызывает редактор списка предсказаний.
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 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(«Use green grip or shake (move) ball to get prediction») еще не реализован в старой версии 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 Free

Понятно, что данный пример — шуточный и практической пользы не имеет, но надеюсь, что он все же кому-нибудь пригодится.
Всем удачного дня!
P.S. На всякий случай предупрежу, что последнее обновление Windows 10 немного ломает x64 версию NanoCAD 8, поэтому весь код тестировался в x86 версиях.
