I was trying to do this on my component. store is a variable pointing to a Dictionary, which as strings as keys and GH_Structure<IGH_Goo> as values. I need a ToBytes(). A little help please?
This is beyond me. I wanted to create an equivalent to the data dam component, with a key pointer to each GH_Structure and it would save with grasshopper file. Is this possible?
Hereâs code for a component which âremembersâ the last data that went through it. So you can connect it to some input, then disconnect it and the data is still there. Even after a file Save/Open cycle.
using System;
using GH_IO.Serialization;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Parameters;
using Grasshopper.Kernel.Types;
namespace TestTreeSerialisation
{
public sealed class PersistentDataComponent : GH_Component
{
public PersistentDataComponent()
: base("Persistent Data", "PData", "Retain the last known data for as long as possible", "Data", "Test")
{ }
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Data", "D", "Data to retain", GH_ParamAccess.tree);
pManager[0].Optional = true;
}
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Data", "D", "Retained or passed data", GH_ParamAccess.tree);
}
public override Guid ComponentGuid
{
get { return new Guid("{20269257-D06D-4C0B-92FB-4704329A1112}"); }
}
protected override void SolveInstance(IGH_DataAccess access)
{
// If the input parameter has persistent data, or is connected to sources,
// then assign the RetainedData from the input.
// Otherwise use the retained data.
var data = RetainedData;
var input = Params.Input[0] as Param_GenericObject;
if (input is null)
throw new InvalidCastException("Input was supposed to be a Param_GenericObject.");
if (input.SourceCount > 0 || !input.PersistentData.IsEmpty)
{
access.GetDataTree(0, out data);
RetainedData = data.ShallowDuplicate();
}
if (data is null)
data = new GH_Structure<IGH_Goo>();
access.SetDataTree(0, data);
}
/// <summary>
/// Gets or sets the retained data.
/// </summary>
private GH_Structure<IGH_Goo> RetainedData { get; set; }
public override bool Write(GH_IWriter writer)
{
// Serialize the retained data if there's something there.
var data = RetainedData;
if (data != null && !data.IsEmpty)
data.Write(writer.CreateChunk("RetainedData"));
return base.Write(writer);
}
public override bool Read(GH_IReader reader)
{
RetainedData = null;
// Deserialize the retained data if it exists.
var chunk = reader.FindChunk("RetainedData");
if (chunk != null)
{
var data = new GH_Structure<IGH_Goo>();
data.Read(chunk);
RetainedData = data;
}
return base.Read(reader);
}
}
}
Thank you!
With your code pattern I improvised this
public class retainer:GH_Component
{
protected Dictionary<string, GH_Structure<IGH_Goo>> storage = new Dictionary<string, GH_Structure<IGH_Goo>>();
// .... some code that fills storage
public override bool Write(GH_IWriter writer)
{
foreach (string k in storage.Keys)
storage[k].Write(writer.CreateChunk(k));
return base.Write(writer);
}
public override bool Read(GH_IReader reader)
{
foreach (var chunk in reader.Chunks)
{
var ghtree = new GH_Structure<IGH_Goo>();
ghtree.Read(reader.FindChunk(chunk.Name));
storage[chunk.Name] = ghtree.Duplicate();
}
return base.Read(reader);
}
}
And of course it fails. IO error upon reopen of the gh file. Object set to null
What am I doing terribly wrong? By the way the test GH_Structure only has int in them.
This is a problem, the reader will contain a lot of chunks which arenât yours. You need to name your own chunks in a way which allows you to find them all without trying ones that donât belong to you.
I recommend naming them all the same and using an index to identify different ones.
I have to go do a recycling run now before they close, but I can tell you later how that might work.
At your leisure of course. Thanks for support!
Btw if I bypass the null reference error with try/catch, I get a âchunk already existâ error in the Write override.
protected Dictionary<string, GH_Structure<IGH_Goo>> _storage = new Dictionary<string, GH_Structure<IGH_Goo>>();
private void WriteTest(GH_IWriter writer)
{
int n = 0;
foreach (var pair in _storage)
{
// Create a chunk with a specific name and an increasing index.
var chunk = writer.CreateChunk("StoredData", n++);
// Store the dictionary name as a string on the chunk.
chunk.SetString("Key", pair.Key);
// Store the dictionary value in a sub-chunk.
var treeChunk = chunk.CreateChunk("Tree");
pair.Value.Write(treeChunk);
}
}
private void ReadTest(GH_IReader reader)
{
_storage.Clear();
// Try and read as many "StoredData" chunks as possible.
// This approach demands that all these chunks are stored under increasing indices.
for (int i = 0; i < int.MaxValue; i++)
{
var chunk = reader.FindChunk("StoredData", i);
// Stop looking once we've run out.
if (chunk is null) break;
var key = chunk.GetString("Key");
var treeChunk = chunk.FindChunk("Tree");
var tree = new GH_Structure<IGH_Goo>();
tree.Read(treeChunk);
_storage.Add(key, tree);
}
}
I have indeed tested thatâŚ
YES it works. Thank you!
Would this be worthy of incorporating in the standard set of components that ships with RH/GH? Fussy people like me may script a generation of things on a âseed-implicitâ Random, and this would provide a way of storing options deliberately.