📜 ⬆️ ⬇️

“Truth in wine” or try programming NanoCAD under Linux (MultiCAD.NET API)

Almost since the release of the first “capable” version of NanoCAD, among the user community there was an acute question about the need to implement this CAD for Linux

Probably, you thought that this article was born, because the developers finally "made it!". I hasten to reassure you - everything is left in their places . I still don’t know anything about the NanoCAD version for Linux. Therefore, we will try to use Wine.

So this short note will be not so much about using NanoCAD in Linux, but about programming for Nanocad in a system other than Windows, and if even more precisely, how I armed myself with Linux Mint, MonoDevelop and tried to build a library (.dll) for free version of NanoCAD using the MultiCAD.NET API.
')
If you're wondering what came out of it, you are welcome under the cat!

image


Part I: ... restless hands


It seems that my new hobby will also ultimately result in a mini-cycle of articles, and since we will refer to past developments, I will cite all previous articles under the spoiler.


At the very beginning, just in case, I’ll clarify that I’m not a programmer, that I can’t be called a “confident Linux user”, and that I’m not connected to the developers of NanoCAD (Nanosoft), so everything that will be lower This is my private and in some places naive non-professional opinion.

It would probably be appropriate to share the source data with which I approached the solution of the problem:

  1. OS - Linux Mint 18.3 "Sylvia" - Cinnamon (64-bit)
  2. Wine - PlayOnLinux (Wine 2.2)
  3. IDE - MonoDevelp 7.1 (Mono 5.2.0)
  4. Hands - "from the priests" (and great curiosity)

Since there are so few people programming for NanoCAD, and among them there are so much extreme and uncontrollable people who are ready to program for NanoCAD under Linix, even less, I think that it will not have much practical value and will be considered only for the purpose of satisfying technical curiosity.

If, after the previous paragraph, you have continued to read it, it means you have an excerpt like Chuck Norris , and this is worthy of respect.



It's finally time to find out what awaits us in this article.

Content:


Part II: trying to run using Wine



It should be noted that the developers, although not directly spending their resources on adapting to Linux, still sometimes worry about us. The impetus to the creation of this article served as their post on Habré which I accidentally stumbled upon - “ Council Repost: running nanoCAD free 3.5 / 3.7 under Linux using Wine” .

Of course, version 3.7 is already completely outdated, and I decided to try a more recent version of NanoCAD 5.1 close to it.

If suddenly you are not at all familiar with Nanocad, then you can read the very first article of the cycle . In short, NanoCAD 5.1, though not an open source program, but still it is absolutely free for commercial use , I personally think that if developers made sane adaptations for Linux, at least with the help of Wine, then thanks to the familiar interface and similarity with AutoCAD, in the functionality of a two-dimensional "electronic drawing board" it could well compete, the same FreeCAD and QCAD (LibreCAD).

But for now, we will be content with what we have, and we have advice from the forum on how to run NanoCAD 5.1 under Wine 1.4 .

Unfortunately, I stumbled upon this material after I finally lost my enthusiasm, so if someone starts up properly after following this instruction, share in the comments.

And I will tell you what happened with me.

  1. I started by installing Wine from the Mint distribution, and there was version 1.6, it ended up with what was said in the above two tips from the articles. Incorrect icon colors and problems with some buttons. Actually, as it turned out, this could have been stopped, because I did not achieve much better results, but in the end I decided to put PlayOnLinux and continue to “revive Frankenstein”.
  2. For starters, I tried version 1.4.1, in which everything should work, and I suspect that it would really work fine, but there is one problem - I just could not install .NET Framework 3.5 (and .NET Framework 3 also ). I tried a lot of things, but as a rule, during the installation process, an error always took off in different places.

    NET Framework 2 is an extreme version that you managed to deliver (the 4th version does not count), with this version of the NanoCAD framework, it even starts to load and, if lucky, will load a large part. Only libraries will not work, primarily those related to the API (which does not suit us).



    The screenshot shows that it is possible to draw and that all colors are displayed correctly, but unfortunately the Netload command does not work, which means that our library will not be loaded in the future, so we go ahead.
  3. Then I tried a bunch of versions of Wine under PlayOnLinux: 1.3.X, 1.5.X, 2.1, even WINE @ Etersoft tried, everywhere there were problems with the installation. NET Framework 3.5.
  4. As a result, I settled on the latest version of Wine available to me in PlayOnLinux - 2.2, under it, by some miracle. NET Framework got up, NanoCAD started up completely, though the colors are still problems. Screenshots of what it looks like later.
  5. The other required prerequisite of Microsoft Visual C ++ 2008 SP1 Redistributable Package (x86), is almost always installed without problems (you can directly the one that comes with the program). Also do not forget to install the development package (SDK) during the installation of NanoCAD
    Sometimes when installing knocks master registration. I have had cases when direct on-line registration failed, then you can take a license file from an already installed version, for example, under a full-featured Windows (in the ProgramData \ Nanosoft \ RegWizard \ Licenses folder) and feed it to the license master.

I also tried to put NanoCAD 8.5 under Wine, it is different from NC 5.1, it already requires NET Framework 4, and there are fewer problems with it, but there I am stuck on installing the LocalDB prerequisite (apparently server components from MS). If someone can run, please share in the comments.

UPD (12/29/17):
As it turned out the 32-bit version of NanoCAD 8.5 It is put and even works! Albeit with brakes.
You just need to install Microsoft Visual C ++ Redistributable Package 2008 and 2012, as well as .NET Framework 4.0
If during the installation you swear that you cannot find Visual C ++, then at the beginning click “OK”, then “cancel” and continue the installation without them.


Part III: we collect the project in MonoDevelop


Having received a more or less workable version of NanoCAD to test our code, we will immediately try to check whether we can rebuild and run a previously written in C # and the assembled library with a pseudo-three-dimensional door in Linux.

To build the project, I chose MonoDevelop (for Windows users, it may be known as Xamarin Studio).

I did not come across it in its pure form (only as part of Unity) and was pleasantly surprised at what looks like everything we need in MonoDevelop.

We open the project already created in MS Visual Studio and try to rebuild.

The full code of the class, so that you do not need to search for anything in other articles, I will hide under the spoiler.

Full code for door under NC 5.1
// Version 1.1 //Use Microsoft .NET Framework 3.5 and old version of MultiCad.NET (for NC 5.1) //Class for demonstrating the capabilities of MultiCad.NET //Assembly for the Nanocad 5.1 //Link mapimgd.dll from Nanocad SDK //Link System.Windows.Forms and System.Drawing //upd: for version 1.1 also link .NET API: hostdbmg.dll, hostmgd.dll //The commands: draws a pseudo 3D door. //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. // V 1.0. More detailed - https://habrahabr.ru/post/342680/ // V 1.1. More detailed - https://habrahabr.ru/post/343772/ // PS A big thanks to Alexander Vologodsky for help in developing a method for pivoting object. using System; using System.ComponentModel; using System.Windows.Forms; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.CustomObjectBase; using Multicad; //added in V 1.1. for monitoring using System.Security.Permissions; using System.IO; using Multicad.AplicationServices; using HostMgd.ApplicationServices; using HostMgd.EditorInput; namespace nanodoor2 { //change "8b0986c0-4163-42a4-b005-187111b499d7" for your Guid from Assembly. // Be careful GUID for door and wall classes must be different! // Otherwise there will be problems with saving and moving [CustomEntity(typeof(DoorPseudo3D_nc51), "b4edac1f-7978-483f-91b1-10503d20735b", "DoorPseudo3D_nc51", "DoorPseudo3D_nc51 Sample Entity")] [Serializable] public class DoorPseudo3D_nc51 : McCustomBase { // First and second vertices of the box private Point3d _pnt1 = new Point3d(0, 0, 0); private double _scale = 1000; private double _h = 2085; private Vector3d _vecStraightDirection = new Vector3d(1, 0, 0); private Vector3d _vecDirectionClosed = new Vector3d(1, 0, 0); public enum Status {closed , middle, open}; private Status _dStatus = Status.closed; //added in V 1.1. (monitor fileds) public enum Mon { off, on}; private Mon _monitor = Mon.off; private string _monFilePath = @"E:\test.txt"; // if it is serialized, you may not be able to copy the object in the CAD editor [NonSerialized] private FileSystemWatcher _watcher; [NonSerialized] private FileSystemEventHandler _watchHandler; [CommandMethod("DrawDoor", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawDoor() { DoorPseudo3D_nc51 door = new DoorPseudo3D_nc51(); door.PlaceObject(); this.TryModify(); // this.Monitor = false; } public override void OnDraw(GeometryBuilder dc) { dc.Clear(); // Define the basic points for drawing Point3d pnt1 = new Point3d(0, 0, 0); Point3d pnt2 = new Point3d(pnt1.X + (984 * _scale), pnt1.Y, 0); Point3d pnt3 = new Point3d(pnt2.X + 0, pnt1.Y+(50 * _scale), 0); Point3d pnt4 = new Point3d(pnt1.X , pnt3.Y, 0) ; // Set the color to ByObject value dc.Color = McDbEntity.ByObject; Vector3d hvec = new Vector3d(0, 0, _h * _scale) ; // Draw the upper and lower sides dc.DrawPolyline(new Point3d[] { pnt1, pnt2, pnt3, pnt4, pnt1 }); dc.DrawPolyline(new Point3d[] { pnt1.Add(hvec), pnt2.Add(hvec), pnt3.Add(hvec), pnt4.Add(hvec), pnt1.Add(hvec)}); // Draw the edges dc.DrawLine(pnt1, pnt1.Add(hvec)); dc.DrawLine(pnt2, pnt2.Add(hvec)); dc.DrawLine(pnt3, pnt3.Add(hvec)); dc.DrawLine(pnt4, pnt4.Add(hvec)); // Drawing a Door Handle dc.DrawLine(pnt2.Add(new Vector3d( -190 * _scale, -0, _h*0.45 * _scale)), pnt2.Add(new Vector3d(-100 * _scale, 0, _h * 0.45 * _scale))); dc.DrawLine(pnt3.Add(new Vector3d(-190 * _scale, 0, _h * 0.45 * _scale)), pnt3.Add(new Vector3d(-100 * _scale, 0, _h * 0.45 * _scale))); } public override hresult PlaceObject(PlaceFlags lInsertType) { InputJig jig = new InputJig(); // Get the first box point from the jig InputResult res = jig.GetPoint("Select first point:"); if (res.Result != InputResult.ResultCode.Normal) return hresult.e_Fail; _pnt1 = res.Point; Stat = Status.closed; // Add the object to the database DbEntity.AddToCurrentDocument(); // added in v.1. return hresult.s_Ok; } /// <summary> /// Method for changing the object's SC (the graph is built at the origin of coordinates). /// </ summary> /// <param name = "tfm"> The matrix for changing the position of the object. </ param> /// <returns> True - if the matrix is passed, False - if not. </ returns> public override bool GetECS(out Matrix3d tfm) { // Create a matrix that transforms the object. // The object is drawn in coordinates(0.0), then it is transformed with the help of this matrix. tfm = Matrix3d.Displacement(this._pnt1.GetAsVector()) * Matrix3d.Rotation (-this._vecStraightDirection.GetAngleTo(Vector3d.XAxis, Vector3d.ZAxis), Vector3d.ZAxis, Point3d.Origin); return true; } 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._pnt1 = this._pnt1.TransformBy(tfm); this.TryModify(); this._vecStraightDirection = this._vecStraightDirection.TransformBy(tfm); // We move the door only when it is closed if not - undo if (_dStatus == Status.closed) _vecDirectionClosed = _vecStraightDirection; else { MessageBox.Show("Please transform only closed door (when its status = 0)"); undo.Undo(); } undo.Stop(); } //Define the custom properties of the object [DisplayName("Height")] [Description("Height of door")] [Category("Door options")] public double HDoor { get { return _h; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _h = value; } } [DisplayName("DScale")] [Description("Door Scale")] [Category("Door options")] public double DScale { get { return _scale; } set { if (!TryModify()) return; _scale = value; } } [DisplayName("Door status")] [Description("0-closed, 1-midle, 2-open")] [Category("Door options")] public Status Stat { get { return _dStatus; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; // Change the rotation vector for each of the door states switch (value) { case Status.closed: _vecStraightDirection = _vecDirectionClosed; break; case Status.middle: _vecStraightDirection = _vecDirectionClosed.Add(_vecDirectionClosed.GetPerpendicularVector().Negate() * 0.575) ; break; case Status.open: _vecStraightDirection = _vecDirectionClosed.GetPerpendicularVector()*-1; break; default: break; } _dStatus = value; } } // Create a grip for the base point of the object public override bool GetGripPoints(GripPointsInfo info) { info.AppendGrip(new McSmartGrip<DoorPseudo3D_nc51>(_pnt1, (obj, g, offset) => { obj.TryModify(); obj._pnt1 += offset; })); return true; } //Define the monitoring custom properties , added v. 1.1: // added in v. 1.1 [DisplayName("Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public Mon Monitor { get { return _monitor; } set { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monitor = value; if (_monitor==Mon.on) { StartMonitoring(); } else StopMonitoring(); } } // added in v. 1.1 [DisplayName("File path for Monitoring")] [Description("Monitoring of file for door")] [Category("Monitoring")] public string MonitoringFilPath { get { return _monFilePath; } set { // Get the command line editor DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; //for hot change filename if (Monitor==Mon.on) { StopMonitoring(); if (!TryModify()) return; _monFilePath = value; StartMonitoring(); ed.WriteMessage("Monitored file is changed"); } else { //Save Undo state and set the object status to "Changed" if (!TryModify()) return; _monFilePath = value; } } } //Define the methods, added v. 1.1: // added in v. 1.1 [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public void StartMonitoring() { DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; _watcher = new FileSystemWatcher(); if (File.Exists(_monFilePath)) { _watcher.Path = Path.GetDirectoryName(_monFilePath); _watcher.Filter = Path.GetFileName(_monFilePath); _watchHandler = new FileSystemEventHandler(OnChanged); _watcher.Changed += _watchHandler; _watcher.EnableRaisingEvents = true; } else ed.WriteMessage("File: " + _monFilePath + " " + "not Exists"); } // added in v. 1.1 public void StopMonitoring() { if (_watcher != null & _watchHandler != null) { _watcher.Changed -= _watchHandler; _watcher.EnableRaisingEvents = false; } } // added in v. 1.1 private void OnChanged(object source, FileSystemEventArgs e) { DocumentCollection dm = HostMgd.ApplicationServices.Application.DocumentManager; Editor ed = dm.MdiActiveDocument.Editor; ed.WriteMessage("File: " + e.FullPath + " " + e.ChangeType); //read new value from file try { if (File.Exists(_monFilePath)) { int mStatus = -1; ed.WriteMessage("File exists "); using (StreamReader sr = new StreamReader(new FileStream(_monFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { if (sr.BaseStream.CanRead) { if (int.TryParse(sr.ReadLine(), out mStatus)) { if (Enum.IsDefined(typeof(Status), mStatus)) { if (!TryModify()) return; Stat = (Status)mStatus; //  if (!TryModify()) return; DbEntity.Update(); McContext.ExecuteCommand("REGENALL"); ed.WriteMessage("Door state is changed"); } else ed.WriteMessage("Incorrect data in the file. Should be in diapason: 0, 1, 2 "); } } else ed.WriteMessage("Can't read file "); } } else ed.WriteMessage("File not exists "); _watcher.EnableRaisingEvents = false; // disable tracking } finally { _watcher.EnableRaisingEvents = true; // reconnect tracking } } } // TODO: There are many shortcomings in this code. // Including failures when working with copying, moving objects and saving files, you can improve it if you want. } 




As you can see the build is successful. Open NanoCAD, open the previously created .dwg file with the door and the sheep (posted on GitHub ).



As you can see everything opens. And even intentionally included in the code error warning made on the basis of Windows Forms opens correctly (although it looks crooked).



Once everything works, let's try to collect, something new and very very primitive, purely for educational purposes. But first, prepare yourself the tools.

In one of the articles I briefly described the process of creating a project for NanoCAD 5.1 in MS Visual Studio , in this I offer you the same brief instruction, only for MonoDevelop under Linux:

  1. Open the IDE and in the File menu, select the item to create a new solution (project).
  2. In the new window, select the type - class library C #

  3. We give him some name.

  4. Connect the libraries MultiCAD.NET, you need to look for them in the Mint folder, in which the virtual disk of our Wine system is organized. The SDK is usually placed in the same folder as the NanoCAD, the plug-in libraries are in the include folder. For this project, we only have enough mapimgd.dll.

  5. Be sure to uncheck the "copy locally" option.
  6. Then, by clicking RMB on the project name in the object inspector, select the options item and set the .NET Framework 3.5 as the final platform, as in the picture.

  7. Then you can write code and collect code with the Build command (F8). Then we will find our library in the debug folder of the project and copy it to the virtual drive “C” or to any other place where Wine has access. It is important that NanoCAD can see your .dll through its open file dialog.

Part IV: the birth of a penguin


You and I have so successfully set everything up, let's write something already. I decided to offer you a very unpretentious code, so that it runs exactly without problems.

Under the spoiler there is a code hiding, which draws us my “highly artistic vision” of the penguin. The class contains only the “Dping” command, which draws simple geometric objects and displays text.

Full code for NC 5.1 in C #
 using System.Collections.Generic; using Multicad.Runtime; using Multicad.DatabaseServices; using Multicad.Geometry; using Multicad.DatabaseServices.StandardObjects; namespace nano { class penguin { [CommandMethod("DPing", CommandFlags.NoCheck | CommandFlags.NoPrefix)] public void DrawFace() { DbCircle body = new DbCircle(); body.Center = new Point3d(200, 200, 0); body.Radius = 105; body.DbEntity.AddToCurrentDocument(); DbCircle eye1 = new DbCircle(); eye1.Center = new Point3d(150, 255, 0); eye1.Radius = 8; eye1.DbEntity.AddToCurrentDocument(); DbCircle pupil1 = new DbCircle(); pupil1.Center = new Point3d(148, 254, 0); pupil1.Radius = 2; pupil1.DbEntity.AddToCurrentDocument(); DbCircle eye2 = new DbCircle(); eye2.Center = new Point3d(250, 260, 0); eye2.Radius = 8; eye2.DbEntity.AddToCurrentDocument(); DbCircle pupil2 = new DbCircle(); pupil2.Center = new Point3d(252, 258, 0); pupil2.Radius = 2; pupil2.DbEntity.AddToCurrentDocument(); DbLine hand1 = new DbLine(); hand1.StartPoint = new Point3d(102, 239, 0); hand1.EndPoint = new Point3d(72, 183, 0); hand1.DbEntity.AddToCurrentDocument(); DbLine hand2 = new DbLine(); hand2.StartPoint = new Point3d(298, 236, 0); hand2.EndPoint = new Point3d(325, 192, 0); hand2.DbEntity.AddToCurrentDocument(); DbPolyline nose = new DbPolyline(); List<Point3d> nosePoints = new List<Point3d>() { new Point3d(171, 222, 0), new Point3d(198, 177, 0), new Point3d(231, 222, 0) }; nose.Polyline = new Polyline3d(nosePoints); nose.Polyline.SetClosed(false); nose.DbEntity.Transform(McDocumentsManager.GetActiveDoc().UCS); //change coordinates from UCS to WCS for BD nose.DbEntity.AddToCurrentDocument(); DbText spech = new DbText(); spech.Text = new TextGeom("Hello Habr!", new Point3d(310, 55, 0), Vector3d.XAxis, "Standard", 25); spech.DbEntity.AddToCurrentDocument(); } } } 



UPD (12/29/17): The code successfully without any problems (well, maybe with a few corrections of pluggable namespaces) will also compile for NC 8.5 if you connect libraries from its SDK, but we won't be able to test it in Linux, because I said before NC 8.5 I didn’t take off at all under Wine . We can in the 32-bit version of NC 8.5.

By the way, our compiled library, then without problems will open in version NC 5.1 running directly under Windows (which is obvious in principle).

So, we transfer our library to the “C:” drive, open NanoCAD, enter the Netload command, select our library (I have ping.dll), and enjoy the spectacle.



As you can see under Wine 2.2, for unknown reasons, in the settings of NanoCAD, options related to the assignment of color (instead of the color value - a cross) are muted; I could not solve this problem. But otherwise the files seem to open, the print dialog works, simple objects are drawn, and the more I have not tested.

Well, in order to make sure that everything is completely cross-platform with you, we will launch our penguin library in NC 5.1 under Windows.



UPD (12/29/17): As already mentioned in the piece of the article added above, I managed to run the x86 version of NanoCAD 8.5 under Wine, so catch the penguin from it as well.



To summarize: it seems that at first glance it is quite possible to directly program in C # for NC 5.1 using the MultiCAD.NET API in Linux, but testing your code is no longer so comfortable.

I understand almost nothing in all issues related to Linux, but it seems to me that it is technically possible to create a container, package the correct version of Wine in it, install an inactivated NC 5.1 there, provide some way for the container with Wine to access external disks, and all happiness

Unfortunately, I’ll not be able to crank it on my own for the foreseeable future, so I’ll have to wait and hope for experienced caring users or NanoCad developers.

But if suddenly one day for myself, something happens, I will definitely share this with you. All success and good week!

Source: https://habr.com/ru/post/344884/


All Articles