Rhino3dmIO as a Portable Class Library (PCL)?


#1

Have you guys considered porting the main C# project in the .NET openNURBS SDK to a Portable Class Library?

Brief rundown on PCLs here, for the uninitiated:
http://msdn.microsoft.com/en-us/library/vstudio/gg597391(v=vs.110).aspx

PCLs are extremely powerful these days, as they can work out-of-the-box in virtually any platform (all modern Microsoft platforms including Windows Desktop via regular .NET, Windows Store Apps, Windows Phone Apps; plus Linux, Mac, Android and iOS via Mono), without any need for separate build configurations. You just need to make sure you’re not P/Invoking Windows API calls to guarantee it works on non-Microsoft platforms.

Portable Class Libraries use a set of reference assemblies referred to as the .NET Portable Subset. It is, as the name suggests, a subset of the core .NET assemblies. That said, Rhino3dmIO currently utilizes some functionalities that are not available in a PCL. Here’s a few things that would need to change:

-Anything referencing the System.Drawing structs and System.Windows.Forms would need to go away or reference something else, since these namespaces impose a platform dependency (only work with desktop applications) and are not included in the subset. Luckily all the Windows Forms usages appear to be restricted to rhinocommon\dotnet\UI and can be easily moved to a separate assembly (it’s perfectly fine to make a regular .NET 4/4.5 assembly which references a PCL).

-The old style of .NET built-in serialization is no longer available (ISerializable interface, SerializableAttribute and related attributes). They’ve been superseded by [DataContract], which maybe you’ll want to switch to anyway.

-The AssemblyResolver class would be pretty much entirely unusable since it’s a plugin architecture implementation, which is strictly forbidden in Windows Store Apps and Windows Store Apps (no runtime assembly loading or filesystem searching). Like Windows Forms, this could just be moved to a regular .NET assembly and used only on desktop platforms.

-Various other things like Code Access Security (CAS) seem to be more restrictive in a PCL and things like SuppressUnmanagedCodeSecurityAttribute are not available. Not sure if there’s an alternative for that attribute in particular.

Let me know if there are any plans to explore PCLs or if there’s anything else you’re curious to know about them.


(Steve Baer) #2

I’m still learning about what we can and can’t do with PCLs. Here’s the current issue; the majority of Rhino3dmIo’s functionality is in native C++ libraries that are compiled for specific platforms and are pInvoked from Rhino3dmIo.

Do you know how PCLs deal with the issue of pInvokes and native libraries?


#3

You can P/Invoke from a PCL the same way you would from a normal .NET assembly. There was, however, a particular issue with Windows Phone 8 where P/Invoke did not work. Thus, DllImportAttribute was excluded from the Windows Phone 8 target and thus the PCL itself when Windows Phone 8 was included as a target. I believe Windows Phone 8 has always been targeted by a PCL by default, ever since it was available… therefore DllImport would have appeared to be missing in your PCL by default.

This issue has been resolved as of this past April with the addition of Windows Phone 8.1 as a target in VS2013 Update 2. Now you target Windows Phone 8.1, but not 8, and everything works fine.

If the native assemblies are platform-specific, you just change the native dll paired with Rhino3dmIO in deployment. On the PCL side, you just have to make sure the EntryPoints (native function names) are the same in each individual native build. Alternatively, you set up a PCL with different configurations with different compilation symbols, and use preprocessor ifs to switch the entry points. This should only be an issue if you are compiling the native dlls with name mangling to handle overloads. Each compiler has it’s own way of doing name mangling. You would have had that issue before if you were P/Invoking a variety of platform-specific native dll’s anyway, so my guess is that issue is already resolved.

P/Invoking native dll’s which were built with Microsoft Visual C++ 2012 or 2013 should work in Windows Store Apps and Windows Phone Apps, by the way (though 2013 may only work with 8.1, not 8).


(Steve Baer) #4

I spent the last hour reading up on this topic and I think it would be great to have PCLs, but I also think it would be pretty hard given the time constraints we already have on getting our products put together. We do have nuget packages for Windows x68/x64 and instruction for building on OSX. I wonder if the new “shared projects” features in Visual Studio may start alleviating the pain that people are going through attempting to target multiple platforms for their projects. We already deal with this issue ourselves since we are using Rhino3dmIo for our iOS and Android (not yet released) builds of our rhino viewer.

That being said, I would love to get some help or even simple pointers on where to take this project with respect to PCL support. It is an open source project on github

Fork it and send me pull requests :smiling_imp:


#5

Done. Beware that there are many files changed. Also beware that there are more SecuritySafeCritical tags required before the example project will successfully run… I can finish those later (there has to be a better way).

Make sure you read Changes.rtf


(Nathan 'jesterKing' Letwory) #6

Hi Colin,

Could you perhaps write your changes in a plain text file? It’s a bit hard to read the .rtf format through the web interface of Github.

/Nathan


#7

The deed is done. I’ve added a header for platform stuff, mentioning that it’s all been upgraded to VS2013 and whatnot.

I’ve also split the existing items into two sections. In the first, I have the items where further action is needed and/or a decision needs to be made. The second section has the issues that, in my opinion, are resolved (such as the easy fixes for the Type/TypeInfo changes in the Portable Subset).


(Steve Baer) #8

This is a rather large set of changes and I don’t feel comfortable just up and modifying code that other developers have come to depend on. I’m also not quite sure how this is really going to help people.

I think this would be best done in a “PCL” branch. I’ll look into what it takes to merge your pull request into a separate branch on github (I’m sure it’s possible, just never done it myself.)


#9

I already call it a PCL branch on my end. You could just create a PCL branch on your repo and I’ll submit a new pull request targeting that.

I agree though that the changes here are significant, some of which are unavoidably breaking changes to the public-facing API. It should probably always be a separate branch.

It will help anybody who might want to utilize OpenNURBS in a Windows Store App or a mobile device. I know the mobile device aspect may sound silly… as nobody is quite ready to start doing CAD work on a mobile phone. However simple viewers could be extremely useful. In the machine tool industry (where I come from), our sales guys would love to be able to preview a prospect’s 3D geometry on their phone. The current “oh put it on a thumb drive and I’ll see if I can open it on my laptop” way of doing things is on its way out.

The restrictions a PCL imposes will also ease the process of maintaining cross-platform maintainability with non-windows desktop platforms. Xamarin Studio directly supports the PCL and PCL/Mono compatibility issues are few and far between, at best (in fact I don’t even know of any specific issues in this regard).


(Nathan 'jesterKing' Letwory) #10

Thanks Colin!


(Steve Baer) #11

We already use Rhino3dmIo for building our iOS and Android viewer apps with Xamarin tools. @dan does the lion’s share of mobile development here so I’m leaving this up to him to make comments, but I know we are currently not compiling Rhino3dmIo as a PCL and we are having success with creating compiles for those platforms. Since you’re in Seattle, you’re more than welcome to swing on by and we can show you what we’ve done with respect to mobile development (we like to share).

Here are my concerns with the code in your pull request:

  • “copyright OMAX” comments are sprinkled through the code
  • You did a global replace of all System.Drawing usages. Rhino3dmIo is a special build of RhinoCommon and many of the classes in the library are surrounded by a #if RHINO_SDK block which removes those classes from the SDK in a Rhino3dmIo build (but not a RhinoCommon build).
  • Many of the files that you modified or deleted are not part of the Rhino3dmIo build configuration. These are RhinoCommon files.
  • I’m not a big fan of adding yet another version of System.Drawing.Point,Font,Size,… to a library. I don’t have a good solution for this yet and I know I’ve had to do this myself for some of the work that Dan has done. Just writing this down to keep it fresh in my head.

I agree it was a bit of a mistake to include references to System.Drawing and Windows.Forms in RhinoCommon since that project was always meant to be cross platform. This was just a mistake that I made early on not realizing the ramifications. In V6, we will have a RhinoWindows assembly and a RhinoMac assembly containing platform specific code. I hope to start work removing the platform specific assembly dependencies from RhinoCommon during the the V6 timeframe.


(Steve Baer) #12

Here’s what I’m thinking of doing for V6:

Create new classes in RhinoCommon that replace Color, Point, Size, Rectangle. These would be given different names than System.Drawing.Color in order to be explicit and to properly map to our namespaces/naming conventions

My current thoughts would be to have structs named as follows:
System.Drawing.Color would become Rhino.Display.Color4b
System.Drawing.Point would become Rhino.Geometry.Point2i
System.Drawing.PointF would become Rhino.Geometry.Point2f
System.Drawing.Size would become Rhino.Geometry.Size2i
System.Drawing.Rectangle would become Rhino.Geometry.Rectangle

RhinoWindows and RhinoMac would have extension methods defined to easily convert these classes to things like System.Drawing.Color or System.Windows.Media.Color or MonoMac.CGColor

All of this is V6 work though since I can’t break the public SDK for V5.


#13

Most of the work I do is for OMAX so I actually have VS add those copyright headers to my cs files by default. I forgot about it honestly, so I’ll remove them.

Adding those structs was the simplest way to get the solution to build without a reference to System.Drawing.

Defining your own structs for these is perfectly appropriate, by the way. Generally what I do is, in the assembly with the external assembly, I define extension methods for each matching struct type that converts it to the other. So you could call something like System.Drawing.Color.ToRC() which returns a RhinoCommon color.

Of course there are many ways to handle multi-platform implementations in C#, other than PCLs, but my finding is that PCLs provide the cleanest and most maintainable method.

I am not sure how much time you spent maintaining per-platform configurations. The primary direct value you could expect to get out of a PCL is a potential reduction of this time.

Keep in mind that a public facing API change would have to happen to make the API usable in a Windows Store App or Windows Phone anyway, regardless of whether or not you are using a PCL.

I still agree that major public facing changes are to be avoided, and that this should remain a separate branch.


(Steve Baer) #14

I wonder if it would just be better to have the “pcl” version reference Splat instead of System.Drawing

I started going down the path of adding my own Color class to RhinoCommon and it just feels like a lot of useless work.


#15

Most of the work of defining a classic color struct is implementing predefined named colors… Which yeah is kind of tedious and arguably is of little value.

I’d say it’s perfectly reasonable to define a color struct minimally, as in just R, G, B, A properties and one or two conversion methods. Especially if it’s only used a means to transport data from one type (i.e. something external that your library doesn’t know about) to another (i.e. the C++ native library expects), in a form that your library already understands.

I’ve seen many systems which opt not to implement a color struct at all, instead expressing colors as Vector4 or Vector3. If your colors are floating point, they are structurally identical to those types anyway. This is typically done in libraries interfacing to GPU APIs and is considered completely acceptable. This doesn’t really apply in your case though.

The important thing here is that the implementation reflects what is appropriate for the role that the library plays. As Rhino3dmIO is a .NET wrapper for a native library, it’s dependencies should be only .NET and the native library.

You could go the splat route, but if that means having to distribute an additional DLL, then it probably isn’t worth it unless you make widespread use of it everywhere. I’m not familiar with that library, by the way.