How do I watch for a component-specific BakeGeometry event?

I am aware that RhinoDoc.ActiveDoc.AddRhinoObject can have functions added (’+’) and subtracted (’-’) from it such that every time a Rhino object is added to the document, the functions are carried out. However, this happens across all components and does not focus on the specific component that triggered the event.

Is there a way to watch for a BakeGeometry event coming from a specific custom scripted component, instead of triggering all the components with that behavior enabled? Say that in the image below, I bake geometry using component A (right hand side) and there’s another component B (left hand side) that has the same behavior when used to bake geoemtry, but I will only raise a warning message for component A and not component B. I’m guessing it’s something like ‘ghenv.Component.’.

warning2

The code I’m using right now to do so is:

# Bake Warning
def bakeWarning(sender, e): 
   ghenv.Component.AddRuntimeMessage(w, 'This object cannot bake geometry outputs.')

# To get rid of previous invocations
r.RhinoDoc.AddRhinoObject -= bakeWarning 
# To add function to AddRhinoObject trigger
r.RhinoDoc.AddRhinoObject += bakeWarning 

Any help’s much appreciated. Thanks!

1 Like

I don’t fully understand. Are you saying the component could bake it’s data but you want to prevent it? Or is it the case that the component can’t bake its data and you want to display an additional warning somewhere with additional information?

Or is it the case that the component can’t bake its data and you want to display an additional warning somewhere with additional information?

It’s this one. Actually, I managed to solve the bake problem of some of the components, which involved rebuilding them from scratch (i.e. from a general python scripted component to the fully working one). I’m wondering if it’s due to a bug with the python scripting component, since I tried using the same class definitions in two different components for TextGoo and drawing the geometry and controlled for all other factors like input and output; one could bake, the other one couldn’t.

However, I’d still like to find out what events to watch for that aren’t Rhino.RhinoDoc.AddRhinoObject, since this one triggers all of the components that ‘cannot bake’, and I may need to give warning messages in the future. There are also some cases where only a single point bakes instead of the whole geometry, and I would like to block bake behavior for those components completely since the points can be distracting. The warning should only be placed on the component that the user tried to bake.

Update: @DavidRutten, I’ve noticed that sometimes, even when I rebuild the component from scratch (with the empty Python scripting component or using another component that’s able to bake) with the same code, inputs and outputs, the component stops being able to bake geometry after a few times. Or when I close a file containing bake-able components, then when I open the same file again the components are no longer able to bake geometry. I’m not sure if this is a bug with the Gh version I’m using or something else entirely? I’m using version 0.9.0076.

I’m sorry but I don’t know enough about the Python component and how it deals with baking to answer that. @piac can you help?

Hi guys,

GhPython does not handle any baking logic. All baking logic is derived from GH_Component. Also, this code:

r.RhinoDoc.AddRhinoObject -= bakeWarning

is not reasonable (Iron)Python code. What are you trying to do?

RhinoDoc events are listed here.

@DavidRutten you might recall having created an elaborate GH_Goo-based definition for Bianchi that I translated to Python for him. According to Bianchi’s report, in some cases this definition cannot bake the geometry.

@Bianchi if you want more help regarding this, we would need a sample that can be debugged. That is, ideally, a minimal sample that shows a repeatable case of the issue, and does not have library dependencies.

EDIT: For completeness, the previous thread was here.

@Bianchi it might be more profitable if we organized a call. It seems this is rapidly becoming a big project but it tries to do a lot of different things.

Hi @piac and @DavidRutten, thank you for the help so far. To clarify on:

is not reasonable (Iron)Python code. What are you trying to do?

  1. Right now, some of my scripted components are unable to bake their geometry so I would like to put warnings on them for users (other people in my research group) as temporary stop gaps while I figure out the baking issue.
  2. Thus, I am trying to watch for when a component tries to bake its geometry so that I can raise a warning message.
  3. I want to raise a warning in the specific component that tried to bake its geometry, but right now using Rhino.RhinoDoc.AddRhinoObjects this causes all components with the warning function inside to raise warnings, rather than just one. That being said, is there a way to limit the warning to a specific component when a Bake event is triggered?

However, if the baking issue can be resolved and the components can bake their geometry, then I would not need the warning message.

As for the rest @piac , I’ll PM you to discuss further.

So, I went through files that you sent in the PM and noticed that you didn’t add the try-except block in the most-encompassing level of the BakeGeometry() function.

I just did and it appears that at least one serious bug was found:

You can see that you imported System not “as sy”, so this will not work.

This is what I mean for most-encompassing try-except:

    #region baking
    def BakeGeometry(self, doc, att, id):
        try:
            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)
            
            return True, id
        except Exception, ex:
            System.Windows.Forms.MessageBox.Show(str(ex))
            return False, System.Guid.Empty

Giulio


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

Hi @piac, thanks for the suggestion. I tried your code snippet and it seems to have fixed the bake issue.

You can see that you imported System not “as sy”, so this will not work.

Okay, that’s my mistake. I did try adding System previously and with the correct import names, but was getting no response then (most likely as I did not understand what to return in case of an exception). I was also stuck on what you meant by ‘most-encompassing level’, plus this bit:

except Exception, ex:
         System.Windows.Forms.MessageBox.Show(str(ex))
         return False, System.Guid.Empty

So for the learning bit: How does returning (False, System.Guid.Empty) change things? From what I understand, is that if the main body catches an exception (like when a Text object doesn’t work or something), then the BakeGeometry function will signal to Gh to not add anything and an empty Guid will be caught. I’m not too familiar with the inner workings of Gh’s work flow, so how do these return values allow Gh to bake the geometry (even the Text Object)?

Update: So adding a ‘try-except’ with a ‘return False, System Guid.Empty’ appears to solve the bake issue completely. Much thanks again to @piac and @DavidRutten for their help.

Here’s the final code snippet, in case it helps anyone else:

import Rhino as r
import Grasshopper.Kernel as gh
import System as sy

class TextGoo(gh.Types.GH_GeometricGoo[r.Display.Text3d], gh.IGH_BakeAwareData, gh.IGH_PreviewData):
    #region construction
    def __init__(self, text):
        self.m_value = text
    
    @staticmethod
    def DuplicateText3d(original):
        if original is None: return None
        text = r.Display.Text3d(original.Text, original.TextPlane, original.Height)
        text.Bold = original.Bold,
        text.Italic = original.Italic,
        text.FontFace = original.FontFace
        return text
    
    def DuplicateGeometry(self):
        return TextGoo(TextGoo.DuplicateText3d(self.m_value))
    
    #region properties
    def get_TypeName(self):
        return "3D Text"
        
    def get_TypeDescription(self):
        return "3D Text"
    
    def ToString(self):
        if self.m_value is None: return "<null>"
        return self.m_value.Text
        
    def get_Boundingbox(self):
        if self.m_value is None:
            return r.Geometry.BoundingBox.Empty;
        return self.m_value.BoundingBox;
        
    def GetBoundingBox(self, xform):
        if self.m_value is None:
            return r.Geometry.BoundingBox.Empty
        box = self.m_value.BoundingBox
        corners = xform.TransformList(box.GetCorners())
        return r.Geometry.BoundingBox(corners)
    
    #region methods
    def Transform(self, xform):
        text = TextGoo.DuplicateText3d(self.m_value)
        if text is None: return TextGoo(None)
        
        plane = text.TextPlane
        point = plane.PointAt(1, 1)

        plane.Transform(xform)
        point.Transform(xform)
        dd = point.DistanceTo(plane.Origin)
        
        text.TextPlane = plane;
        text.Height *= dd / sqrt(2)
        return TextGoo(text)
        
    def Morph(self, xmorph):
        return self.DuplicateGeometry()

    #region preview
    def get_ClippingBox(self):
        return self.get_Boundingbox()
        
    def DrawViewportWires(self, args):
        if self.m_value is None: return
        args.Pipeline.Draw3dText(self.m_value, args.Color)
      
    def DrawViewportMeshes(self, args):
        # Do not draw in meshing layer.
        pass

    #region baking
    def BakeGeometry(self, doc, att, id):
        try:
            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)
            
            return True, id
        except Exception, ex:
            sy.Windows.Forms.MessageBox.Show(str(ex))
            return False, sy.Guid.Empty

Ehm, nice! When you rename an import, the interpreter still has the old variable within globals(). We added code to WIP so that pushing “Test” will get rid of old variables. For now, in 5, you’ll have to be careful when renaming variables. I’d suggest switching to WIP if you see this happening often.

Also, you should learn more about try/except. https://docs.python.org/2/tutorial/errors.html It was vital and very easy to understand the bug once the exception would not unwind the stack into Grasshopper. Basically, anything going bad within try will be handled in the except. Of course, if the try is not fully encompassing (fully containing the block of code) then it will never kick in. This is especially useful when writing code that works with events and does no happen in “Grasshopper solution time”.

1 Like