// M code must ensure that the only types that can arrive in this function are ones that are currently handled here. public static bool ReadAndStoreResult(string type, int index, IKernelLink link, IGH_DataAccess DA, GH_ParamAccess accessType, GH_Component component) { try { switch (accessType) { case GH_ParamAccess.item: { object res = ReadSingleItemAsType(type, link); DA.SetData(index, res); break; } case GH_ParamAccess.list: { ILinkMark mark = link.CreateMark(); int length = 1; bool isList = false; try { length = link.CheckFunction("List"); isList = true; } catch (MathLinkException) { link.ClearError(); } finally { link.SeekMark(mark); link.DestroyMark(mark); } if (isList) { IList list = ReadListOfItemsAsType(type, link); DA.SetDataList(index, list); } else { object item = ReadSingleItemAsType(type, link); DA.SetData(index, item); } break; } case GH_ParamAccess.tree: { // Quick and dirty method. Only support numbers. Read as Expr, then use Dimensions, etc. // TODO: Handle objects. See how it is done in ReadSingleItemAsType(). // This read-as-expr method shows problems for tree type. It would be better, for tree, to not use // the expr, but rather re-read the data from the link (as is done for item, list). In the curremt // method, I don't see any good way to handle "Any". // WAIT, IT'S WORSE. Expr.AsArray() only does Integer, Real, and only up to depth 2. ILinkMark mark = link.CreateMark(); try { Expr e = link.GetExpr(); int[] dims = e.Dimensions; if (dims.Length == 0) { link.SeekMark(mark); object res = ReadSingleItemAsType(type, link); DA.SetData(index, res); } else if (dims.Length == 1) { link.SeekMark(mark); IList list = ReadListOfItemsAsType(type, link); DA.SetDataList(index, list); } else if (dims.Length == 2) { // This code could be cleaner with GH_Structure, but I dont quite understand that class... switch (type) { case "Number": { DataTree <double> tree = new DataTree <double>(); Array dataArray = e.AsArray(ExpressionType.Real, 2); for (int i = 0; i < dims[0]; i++) { GH_Path pth = new GH_Path(i); for (int j = 0; j < dims[1]; j++) { tree.Add((double)dataArray.GetValue(i, j), pth); } } DA.SetDataTree(index, tree); break; } case "Integer": { DataTree <int> tree = new DataTree <int>(); Array dataArray = e.AsArray(ExpressionType.Integer, 2); for (int i = 0; i < dims[0]; i++) { GH_Path pth = new GH_Path(i); for (int j = 0; j < dims[1]; j++) { tree.Add((int)dataArray.GetValue(i, j), pth); } } DA.SetDataTree(index, tree); break; } default: // Can't handle this. return(false); } } else { // TODO. At least set a RuntimeMessage before returning false. return(false); } } finally { link.DestroyMark(mark); } break; // Problem with the read-and-fill-element-by-element approach is that this is a GH_Structure<IGH_Goo>, // so I need to create an IGH_Goo pbjectout of every piece of data. But there are zillions of IGH_Goo // objects out there. I don't want to map every object to its IGH_Goo type (e.g., Circle --> GH_Circle). // For lists, I could simply create a List<anything> and apparently it gets converted to the appopriate // IGH_Goo object later on. But with trees, I don't see the corresponding technique. Compare the signature // of DA.SetDataList() and DA.SetDataTree(). /* * ILinkMark mark3 = link.CreateMark(); * int length = 1; * try { * length = link.CheckFunction("List"); * // Don't catch; allow to fail. Must be a list arriving for tree results. * } finally { * link.DestroyMark(mark3); * } * GH_Structure<IGH_Goo> structure = new GH_Structure<IGH_Goo>(); * GH_Path path = new GH_Path(0); * int pathLen = 1; * int pathIndex = 0; * for (int i = 0; i < length; i++) { * if (isListWaitingOnLink(link)) { * path. * } else { * structure.Append( * } * } */ } } } catch (Exception) { component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Unexpected type of result from Wolfram Engine"); link.ClearError(); link.NewPacket(); return(false); } return(true); }