Maintaining variables between commands


#1

I am using VB.net and rhinocommon to develop a footwear plugin. I’m trying to figure out the best way to structure it as far as variables and so on…

One of the things the plugin does, for example, is to design a foot orthosis about a foot model. The orthosis is created in several steps (commands). First landmarks on the foot are located (in the form of rhino point objects), next the orthosis outline is created (curves), then the surface of the orthosis (surface), then the body of the orthosis (polysurface).

I need to keep track of these objects between commands that are run. A simple way I used to do this when using rhino script was to give the object a name (object properties) and then search for it via this name every time a script was run.

When I started developing in RhinoCommon, I got some advice, and tried to get a bit more advanced with this. I created a class called foot and another called orthotic and these had public properties such as foot.FirstBallJointID (a landmark on the foot as a rhino point object). I created public instances of the classes for left and right feet within a module so they would be maintained between running various commands. I also had properties that when called returned the geometry for the objects.

A problem with this method is that if my user saves the file and comes back later to work on it, a command has to be run that populates all the properties based on object names (similar to what I used to do for Rhino Scripting). So that gets me thinking I should have just stuck with that method in the first place… But perhaps it is slow to have to populate a lot of properties every time a command is run? And I need some variables that cannot be contained in Rhino Objects, like what gender the current subject is.

But probably the biggest problem though is that this structure is a bit difficult to understand. I will have other people working on it in the future and it has to be easily understood for someone coming from RhinoScript.

I don’t have an exact question here… perhaps the best way to help me would be to explain how you setup plugins for variables and how they are maintained between commands. And any general advice and thoughts in this area is appreciated!

Thanks,
Sam


(Menno Deij - van Rijswijk) #2

The plug-in can write and read data to and from 3dm files. This is done in the WriteDocument (and ShouldCallWriteDocument) and ReadDocument methods that can be overridden in your plug-in class.

For more info, see http://wiki.mcneel.com/developer/rhinocommonsamples/userdata in the first part (Document data).

Rather than just a name, you can also attach data to any geometry object by UserString, UserDictionary or UserData. This is explained in the same page in the second part. For my purposes I have only used UserDictionary which is flexible enough to meet all my needs so far.


#3

Hi Menno, that solves the problem of saving data between design sessions, thanks!

What about the way I track rhino objects between commands: an object’s ID is in a public property of a class (a public instance of the class is created in a module). So when I access it, I do something like SomeModule.SomeClass.SomeRhinoObjectID and then when I want to access the geometry for the object, I have a readonly property (same name as above but without the ID) that retrieves the geometry from that ID.

Does this seem clumsy? How would you structure a plugin that has to keep track of objects between commands?

Thanks,
Sam


(Menno Deij - van Rijswijk) #4

As long as the object ID does not change when the user performs an operation on your object, it is not clumsy in my opinion. But, if for example, when you split, trim or otherwise modify the object, its ID may change. This will leave you with an object that has an “old” ID that is no longer current. I don’t know if this is the case in your application, but it may be a problem.

What we typically do is not to store an ID, but let the user click the geometry that needs to be modified in the command. That way the input geometry is always up-to-date prior to the command running. Then, the user changes the geometry in the command using parameters etc. and we show a preview of the new geometry using a DisplayConduit. Lastly, if the user is happy with the changes he/she accepts the changes and they are propagated to the document.

We have called this the “ICC pattern”, for Identify-Change-Commit:

  1. Identify: the user identifies the geometry that must be changed
  2. Change: the user can set several parameters and is shown a live preview of the changes made
  3. Commit: the user accepts the changes and the geometry is updated in the document.

The advantages of this approach are that

a) we don’t need to track geometry because it is identified by the user.
b) any changes are shown in a preview display conduit. No need to update geometry in the document while the user is making choices.
c) we only make changes to the document at the end of a command. This means that the user can cancel a command at any point and the document remains unchanged.

Hope that helps!

All the best,
Menno


#5

I do like the idea of asking the user for the object prior to running a command, that really simplifies things. Unfortunately, for my application this would be too much work for the user; I use more than 10 foot landmarks (rhino point objects) as inputs for the orthosis design command. I couldn’t ask the user to click all these, so I need to have them stored between commands.

Anyone have to keep track of objects between commands for a plugin? How do you do it?

Thanks,
Sam


(Menno Deij - van Rijswijk) #6

The simplest way is to add some read/write properties to your plug-in class. The plug-in is guaranteed to be instantiated only once and is available while Rhino runs.

class MyPlugIn : PlugIn
{
    Point3d Location1 {get;set;}
    Point3d Location2 {get;set;}
    //etc.
}

class MyCommand : Command
{
    protected Result RunCommand(RhinoDoc doc, RunMode mode)
    {
       MyPlugIn p = MyPlugIn.Instance;
       Point3d l1 = p.Location1;
       Point3d l2 = p.Location2;
    }
}

(Steve Baer) #7

Commands are also guaranteed to be instantiated only once so you could place the variables in the commands if that makes more sense for what you are doing.


#8

Hi Steve,

I think I get what you are saying: Commands are public classes and are only created once, so once I run the command, all its variables will be accessible from then on in all other commands I run, right?
So this is an alternative to putting the variables in a module? I guess I have to decide between the two… If you have any thoughts as to which approach is better, or if there is a different way to make variables global to an entire plugin and all its commands, please let me know.

That’s the first half of the problem (where to put variables so they can be global to all commands), the second half is how to keep track of rhino objects between commands: object ID, rhino object, or search for it via its object name in the document. Though the last one seems kind of crude, what I like about it is that everything is saved right there in the standard object properties for the rhino file and there is no need to do any extra development for dealing with saving, closing, and reopening the rhino file. How do developers typically deal with this, is this last approach too crude?

Thanks,
Sam


(Steve Baer) #9

I would stay far away from modules for variables.

Use the Command and Plug-In classes and have them hold instances of non static (shared) variables. Use properties for accessing these variables if you need to use them at all from outside of the command or plug-in class.


#10

Thanks Steve,
Previously I didn’t fully understand what shared meant. I think it solves the problem, just simply put my variables in any class as long as they are shared. So for example, I have a landmark called FirstBallJoint, so I just put it shared and public in a class called Foot along with the many other foot related variables. Sound OK?

I’m going to have to do some convincing as to why I am switching from modules to shared variables in a class, in my mind it’s just cleaner, but perhaps you have a better reason?

As far as the second problem, how to keep track of rhino objects between commands, do you have any thoughts?

Thanks,
Sam


(Steve Baer) #11

I wasn’t recommending using shared variables; that’s pretty much the same as global shared variables on modules. I was recommending using instance variables on your plug-in and command classes.


#12

OK, I’ll put the variables in the plug-in and command classes. I have a lot of variables for my Rhino objects, they can be categorized as related to the foot model, shoe last model, orthosis model, shoe model etc. So I previously I have had classes called foot, orthosis, etc. that contain the variables. I don’t want to just put all the variables directly into the plugin class or a single command class. Should I have multiple command classes (foot command class, orthosis command class etc.) or create an instance of the foot, orthosis, etc. classes within a single command or plugin class?

I thank you sincerely for your patience. It’s become clear we are taking the project to the next level, and it’s a good time to make sure the plugin has a good foundation.

Still hoping you have advice on best practice for keeping track of rhino objects.

Thank you kindly,
Sam


(Steve Baer) #13

Hi Sam,
I don’t think I’m being clear with my suggestions. I would keep the classes that you already have for maintaining data. I was only suggesting that the location where instances of these classes were created and maintained were in your plug-in and command classes. Most likely your main “model” classes will be kept in your plug-in class and things like sticky setting variables were kept in commands in cases where you wanted to try and keep some sort of state between runs of a command.


#14

OK, gotcha, I will set it up that way, thanks!

As far as how to track rhino objects, I’ll tell you how I have been doing it, then maybe you’ll see why I’m asking about it:

For every rhino object I have two properties, one for the ID and one for the geometry. The geometry property is readonly and, when called upon, gets the object geometry from the corresponding object ID property. Closing and reopening the rhino file presents extra challenges but I see there are ways to deal with that as Menno suggested. But more to the point it seems a bit clumsy to always have two properties for each object.

I was thinking an alternative would be to have a single property for each object. The get for the property provides the geometry for the rhino object in the rhino document with the matching rhino object properties name. The set for the property deletes the rhino object with the matching rhino object property name and then creates a new rhino object and attributes it the matching rhino object property name. Thus closing and opening the rhino file is dealt with and there is only one property for each rhino object.

But maybe I am overlooking something or this is truly a crude method that more experience developers would cringe at :frowning: What do you think, is it OK or is there a better way?

Thanks,
Sam


(Steve Baer) #15

I’m not really going to be able to make any useful suggestions here without seeing any code. It is getting pretty difficult to imagine what you are describing.


#16

Hi Steve,

I’ve attached the basic structure for the plugin.

Thanks!
SamWatFootStructure.zip (463.1 KB)


#17

Hi Steve,

Sorry to pester you on this… have you had a chance to look at the code? It’s really simple, I stripped it down to the basics so you could quickly see what I’m talking about.

Thanks,
Sam


(Steve Baer) #18

Sorry, haven’t had the time yet. Hopefully I’ll be able to look through my support list on discourse in the next couple of days.


#19

Hi Steve,

Have you had a chance to take a look?

Thanks,
Sam


#20

Hi Steve, no need to open the entire project I sent you earlier, my question can be simplified to this:

Currently I have two properties for keeping track of rhino objects in my plugin, one for geo, one for ID:

Public Property MPJ1ID As System.Guid
Public ReadOnly Property MPJ As Rhino.Geometry.Point3d
Get
Return TryCast(doc.Objects.Find(MPJID).Geometry, Rhino.Geometry.Point).Location
End Get
End Property

I have been thinking a more clever way to do this is to only have one property that uses the objects name in the document, thereby dealing with saving, closing, and reopening:

Public Property MPJ As Rhino.Geometry.Point3d
Get
’get MPJ geometry from point in rhino document with object property name “MPJ” on appropriate layer
End Get
Set(ByVal Point As Rhino.Geometry.Point3d)
'Delele point in rhino document with name “MPJ” and recreate it at Point, attributes set to correct name, layer
End Set
End Property

Which of the two methods seems more robust to you? Do you have any suggestions for a better way to keep track of both rhino objects and their geometries between plugin commands?

Thanks,
Sam