Grasshopper

algorithmic modeling for Rhino

Hi David.

I noticed in a lot of your components, there exists data persistence properties. For example, your Swatch component, you can copy and paste this component, and the cloned component would inherit the data of the one copied.

Sample Swatch:

Result of Copy and Paste:

As you can tell the new Swatch inherits the color of the previous one.

An observation I made is that, if you open the Grasshopper file through the .ghx extension, and navigating to the Swatch XML snippet, you can find this idea of data persistence in it, exampled here:

_________________________________________________________________________

I tried to emulate this data persistence through hardcoding input parameters through forcing a RegisterInputParam to contain specific data. Personally I cannot think of a way to do this without using a dummy Input.

Code snippets that relate to this input pseudo data persistence:

I have a component where I would like to inherit these data persistent properties - how do I achieve this David the Grasshopper God?!!

Views: 1651

Replies to This Discussion

If I'm not mistaken, this is actually a property of the component overriding the "Read" and "Write" functions to handle passing the data to be saved into a GH_IWriter and retrieving the data from a GH_IReader. here is a simple example of setting/retrieving a boolean value called "Active", in C#:

public override bool Write(GH_IWriter writer)

{

writer.SetBoolean("Active",m_activeValue); //where m_activeValue is a boolean belonging to your class

return base.Write(writer);

}

public override bool Read(GH_IReader reader)

{

m_activeValue = reader.GetBoolean("Active");

return base.Read(reader);

}

It's my understanding that these functions get called every time you're copying and pasting a component, and also when files are saved/read. 

I'm typing this code without actually testing it, so use at your own risk. 

You're correct, but I don't know c# to check if there are any typos.

Typos? If you make a typo in code it won't compile. If you make a typo in the names of the archive fields then it won't work without any error message.

If you're worried that you can't type "Active" twice without misspelling it once, you can use a global or a const string that is used in all (de)serialization:

private static readonly string activeField = "Active";

--

David Rutten

david@mcneel.com

I'm not 100% by what is meant by "data persistence". If you want to store data so that it's there the next time you open the GH file, then you need to write and read that data using the Write() and Read() overrides.

However Write() and Read() are only called when the component is (de)serialized. Basically during Save, Open, Copy, Paste, Undo and Redo. If you need data to survive between calls to SolveInstance, then you should store that data in a class level variable:

private string _someData;

You can access that field from within SolveInstance and from within any other non-static method on your GH_Component class. To make this data then persist across file reads, you should add:

writer.SetString("SomeData", _somedata);

to the Write() override and:

if (reader.ItemExists("SomeData"))

  _someData = reader.GetString("SomeData");

to the Read() override.

--

David Rutten

david@mcneel.com

No - these work as before.
GetData and SetData are about getting data to and from parameters while the component is in use as part of a definition; read and write are about holding on to data and settings during serialization and deserialization, which occur during file saves or copy paste operations, for example.

I don't think you understand it yet.

Write() is called by Grasshopper when you Save a file, create a new Undo record which includes your component and when you Copy the component.

Read() is called by Grasshopper when you Open a file, perform an Undo operation that involves your component and when you Paste.

Read and Write may involve an Xml file, but that's irrelevant. (De)serialization in Grasshopper works with the GH_IO dll, which provides a type-safe hierarchical database in which values and other databases can be stored.

Values and sub-databases are always stored by name and index (index is optional). So typically when you want to add a value to the database, you use something like:

writer.SetString("ValueName", index, _myData);

where index is some integer (again, this is optional so you don't have to use an index) and _myData has to be of type System.String. Note; if there is already an item with "ValueName" and the same index, you'll get an error.

Conversely, the reverse of this operation is:

_myData = reader.GetString("ValueName", index);

You may want to check whether or not that entry actually exists first, unless you can be 100% certain that it must be there.

What happens to this database after you write your values is hard to say. Perhaps it will be written to a GH or GHX file. Or maybe it will just be kept in memory for some time, or maybe it will immediately be deserialized... you don't know and you shouldn't have to care.

All of this is just (de)serialization. It's Grasshopper's way of making duplicates of documents and components and data and possibly storing those duplicates on a persistent medium such as a hard-drive.

It has nothing to do with the component being active on the canvas or responding to menu clicks. That's a completely different kettle of fish.

If you want to have a custom variable in your component, declare it like you have before:

private string _myVariable = "defaultValue";

You can access this variable from within menu click handlers, from within SolveInstance(), from within your Read() and Write() methods etc. etc.

If you do not implement Read() and Write() and store the value using the writer and reader objects then the value assigned to _myVariable will no longer be there when you open the file later. It will also not survive a Copy/Paste cycle and it may not survive an Undo/Redo cycle.

--

David Rutten

david@mcneel.com

You have to store the value with a certain name. And thenyou retrieve the value using the same name. So however you end up deciding what name to use, it must be the same string in Write() and Read().

--

David Rutten

david@mcneel.com

There is no way to store lists in one go, except byte-arrays. That's primarily why the index is also supported. So if you want to store a list, you should typically write something like:

writer.SetInt32("MyListCount", myList.Count);

for (int i = 0; i < myList.Count; i++)

{

  writer.SetXXX("Item", i, myList[i]);

}

Incidentally if you're worried about conflicting names for different items, you can create a sub-database to store some data. For example:

GH_IWriter listWriter = writer.CreateChunk("MyData");

and then use listWriter instead of writer to store data.

--

David Rutten

david@mcneel.com

RSS

About

Translate

Search

Photos

  • Add Photos
  • View All

Videos

  • Add Videos
  • View All

© 2021   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service