Creating text objects and outputting them as normal Rhino geometry

With reference to the post generate text and use outlines in grasshopper… is it possible?, I am trying to output text visually and as an actual output in Rhino 5 using a Grasshopper scripted component. Basically, it should act like any Rhino.Geometry: it can be previewed and is removed from the display when the Preview is removed, and it can be baked using the component. It can also be used as input to another scripted component.

From what I understand based on the post above, it is currently not possible in Rhino 5 to make TextObjects that function similar to Rhino.Geometry (not Objects.Add), i.e. preview-able and bake-able, and not immediately added to the Rhino Document. Am I wrong in this understanding? If someone could help me out with which functions to use or by giving an example of a workaround, I can adapt it from there. I am coding in Python.

I’ve attached a file containing how far I’ve gotten so far, based on the gh file posted by Mostapha in the linked post. What am I doing wrong/what am I missing here?

textCurve_edit.gh (7.3 KB)

1 Like

You’re mostly correct. You cannot override the Bake behaviour of a scripted component (although you can override the preview). That means that if you want baking/preview to work, you’ll have to output text data which supports these properties. This is possible, but can lead to problems since the data will be defined inside a script component. When that component is no longer in a file, you lose access to it. This becomes problematic only in those cases where the data is internalised.

Okay, so that being said, is there a way for me to output Text as objects similar to Rhino Geometry? i.e. preview-able and bake-able, aside from turning them into polycurves by exploding the underlying geometry. Technically, I would be passing them as input/output from component to component.

BakeableText.gh (16.1 KB)

1 Like

Thank you, @DavidRutten ! I’ll post up the Python component here if and when I can adapt it. I think Rhino.Display.Text3D seems to be the data type I was looking for.

P.S. I think I should clarify it a bit further. Is it possible to do such text object creation and output without doing a function override in GhPython? This is since I’m using Rhino 5 (which is more commonly used by students since Rhino 6 is not yet out) though I am aware that overriding Bake and Preview via Python will be available in Rhino 6. In particular, I’m using GhPython so this makes overriding difficult (or impossible, but you know more than I do on that front).

Also, I am a bit confused about this bit of code. Does it basically work as an override to the Bake Geometry function?

#region baking
bool IGH_BakeAwareData.BakeGeometry(RhinoDoc doc, ObjectAttributes att, out Guid id)
{
  id = Guid.Empty;
  if (m_value == null)
    return false;

  if (att == null)
    att = doc.CreateDefaultAttributes();

  id = doc.Objects.AddText(m_value, att);
  return true;
}

Python is beyond my remit I’m afraid. But note that the script component isn’t doing anything special, it outputs an instance of a class which provides the preview and baking functionality. As long as you can generate your own .NET class type via Python it should be possible.

It’s not an override. The class I created implements the IGH_BakeAwareData interface which demands a function with this signature. Eventually, when a parameter containing this data is baked, all data in that parameter is asked to bake itself. Ultimately, previewing and baking is a per-datum operation, parameters merely relay that functionality.

@DavidRutten, there are a number of overrides in the code you provided. Are they necessary if I convert the code to Python, in particular Boundingbox and GetBoundingbox?

As long as you can generate your own .NET class type via Python it should be possible.

From what I understand, I’d need to use the clr module to import the .NET class (based on the component code) into the ghPython component. However, what I’d actually like to do is write the code entirely in Python. Given that there are a number of overrides in the component sample and that I’ll be handling other geometry aside from Text (e.g. lines, points), is it possible to do this in ghPython alone?

Don’t know, too python. @piac can you enlighten us?

Hi guys

yes, this is possible in GhPython, also in Rhino 5, as the stuff that needs to be overridden is not in the GH_Component/GH_Script class itself. Here it is, David’s code, translated to IronPython.

BakeableTextGhPy.gh (15.7 KB)

5 Likes

Thanks again for the help, @DavidRutten and @piac. Funnily enough I had just finished translating it to Python when your reply showed up. In particular, I was struggling with returning the right data type from the function, since I’d only get the class instance back when I would store the function call in the output A. It does exactly what I was aiming to do; I’ll come back and post another file here if the solution would be helpful for others.

I’m curious about this bit of inheritance though: GH_GeometricGoo[rh.Display.Text3d]. Does this mean the GeometricGoo instance stores a Rhino Text3d data type? If so, what is the class inheriting in terms of Grasshopper structures here? This appears to be the key to getting back the Text Object itself instead of just the class instance, aside from also returning the id of the text object in the BakeGeometry definition and marking DuplicateText3d as a static method.

Yes.

Most goo types are wrapper types that provide IGH_Goo functionality for pre-existing data (such as integer, strings, booleans, Curves and, in this case, Text3d). Because of this common wrapper nature, the abstract GH_Goo<T> class already provides a place to store the underlying type and exposes it through the Value field.

It provides ways for Grasshopper to duplicate, format, convert and (de)serialize the data, amongst a few other things. It also allows you to tack on previewing and baking by implementing more interfaces, which you cannot do on Text3d itself because you did not define that type.

Hi @DavidRutten and @piac, I’ve adapted the TextGoo class. While the previewing is working fine, I have run into the following issue regarding baking, which does not happen all the time but only in certain cases. I’ve checked both types of components (the ones where Baking works, and the ones where it doesn’t), and there doesn’t appear to be any difference in the TextGoo code. However, when I try to bake any objects using, for example, the Move Shape component attached here, nothing happens, while it is still possible to bake geometry with S2G. I’m still getting back the geometry objects themselves, even with the components where no baking happens when I try to bake them.

I’ve attached the components here for your reference. Running them requires a custom external library and other custom scripted components that would require you to do some installation, so I’ll just post just the scripted components here for now since I don’t think the external library is necessary to illustrate what may be wrong in my code. Please let me know if you think otherwise.

bake_text_components.gh (7.2 KB)

What the components are supposed to do: Basically, in these two components S is a collection of data (coordinates, attributes) about line segments, text and points based on Rhino geometry, and it is turned back into Rhino Geometry through these components (which is why there is a G output). They’re stored in a custom data structure because that works best with the operations I want to perform on them (i.e. shape grammar, shape matching).

Thanks again for your answers, it’s helped me a great deal with learning how to script in Rhino (Python) better.

I haven’t tried your script, but if baking does not work, you should see if exceptions occur (use Python’s try and except maybe in conjunction with System.Forms.MessageBox.Show(message). This is a simple solution to the fact that baking does not happen at solution-computation time, so printing does not work, as the result of it is not gathered by any solution-collection code.

Please let me know if you have more questions.

Hi @piac, so I’ve tried to put the following in the BakeGeometry method of the TextGoo class:

try:
        message = '!!!'
        System.Windows.Forms.MessageBox.Show(message)

        return True, id
except:
        message = 'aaa'
        System.Windows.Forms.MessageBox.Show(message) 

And it does return a message box for one of the components (the one that does bake geometry successfully, called Shape), but not for any of the others. TextGoo has the same implementation across all of the components, so I’m not sure what next step to take since some of them can bake geometry and some can’t. I’ve attached the components here. Shape can bake components, Scale Shape can’t.

bake_text_test.gh (9.9 KB)

Hi @piac, I apologize if the solution is actually something very obvious as I’m not that skilled with coding in RhinoCommon beyond trial and error with what’s on the developer’s guide, but could you perhaps suggest another way of observing what happens when I try to bake geometry, or maybe give a code example? Right now I’m able to get a single point to bake with the components ‘that are unable to bake’, but none of the LineCurves or the Text objects. I’ve attached a file here with two sample components. They have the same TextGoo class.

I’ve also observed, that when I remove the TextGoo class, the components which are unable to bake geometry work again, so it could be that the BakeGeometry ‘override’ is doing something else in those particular components (?).

bake_test.gh (19.8 KB)

To betterillustrate what these objects are supposed to do, here’s the external library ('sortal) I was talking about. Just running the batch file inside should install it in Rhino’s IronPython, and the components will then work. (It’s still under development)

sortal-setup (8).zip (1.7 MB)

Hi Bianchi

this is really a big project to ask me to debug for you. A common general rule is: whoever writes it, also debugs it.

My guess is that an exception is thrown before the code can bake anything, or something wrong is passed as a result of the function and blocks baking. Also, invalid geometry does not get added to Rhino’s documents, so that could also stop the baking process.

I looked and I could not see the batch file, what am I missing?
Also, if I really have to run all this, could you please make this a simple folder to be added to sys.path (or _EditPythonScript’s Options → Module Search Paths) so that removing it afterwards is a simple folder deletion?

I hope this helps, at least a little bit,

Giulio


Giulio Piacentino
for Robert McNeel & Associates
giulio@mcneel.com

Hi @piac,

that’s particularly why I did not prefer to post the library up here, but unfortunately without it I could not better explain how the components worked. My apologies if it made things more complicated. That being said, I do realize this is a bit of work to ask you to do if you had to install the external library, so please disregard the install file.

I’ll just ask for help about catching the Bake Error as that would be more efficient.

Thanks for the suggestion. This definitely narrows down the causes, as only some objects get baked (sometimes just the text). Running the components does not seem to output invalid geometry (as far as I can tell when I connect it to Panel or other components). However, I’ve generated invalid geometry before and they did not even show up in the Rhino view port. In the cases where I am able to fully generate results, I’m still unable to bake the geometry.

Where do you suggest I could put a System message to watch for errors? I’ve tried placing them inside the BakeGeometry ‘override’ in the TextGoo class, but any potential invalid geometry (or even geometry that is visible as a preview) doesn’t seem to reach that point so I don’t get any response.

Is there a component ghenv or Grasshopper.Kernel variable or method/process that collects the geometry created by the component (that would eventually be baked when Bake Geometry is called) that I could look inside of to track invalid geometry?

It’s just that I tried but there was no batch file to install it…

You should put the try: at the most-encompassing level in the method, and also return True (if the method requires) so that baking is “successful” even if things go wrong. Does it make sense?

Hi @piac, thanks for pointing it out.

Hmm, okay, I’ll add it here then if it helps explain the components any.

components.zip (1.8 MB)

The batch file is inside the folder components → sortal-setup → setup.bat. It’s more a copy and paste method, and it pastes the external library (Sortal) plus other libraries like enum, future and pasteurize inside C:\Program Files (x86)\Rhinoceros 5\Plug-ins\IronPython\Lib\site-packages in your desktop, so that’s where you can find them when deleting them later on.

Hmm, so something like this?

#region baking
def BakeGeometry(self, doc, att, id):
    id = sy.Guid.Empty
    
    if self.m_value is None:
        return false, id
    
    if att is None:
        att = doc.CreateDefaultAttributes()
    
    id = doc.Objects.AddText(self.m_value, att)
    try:
        System.Forms.MessageBox.Show('!!!')
        return True, id
    except:
        System.Forms.MessageBox.Show('???')
        return True, id