Meta Grasshoppering II - Dynamic layout

Here's a fun example of 'meta-grasshoppering'.  We are going to lay out some panels (in my opinion one of the most versatile components in Grasshopper) dynamically via a Python component and play around with their positions, colours and groupings.

The video above demonstrates a set of text panels being controlled via a graph mapper and a gradient control. The generation, manipulation and subsequent deletion of the text panels takes place from the Python component on the far right. Let's look at selected parts of the code. As usual, the full definition can be downloaded below (you will need to have installed the Python component)

def create_panel(position, color):
    """ Returns a small panel """
    panel = gh.Kernel.Special.GH_Panel()
    panel.Properties.Colour = color
    panel.Properties.DrawPaths = False
    panel.Properties.DrawIndices = False
    panel.Attributes.Pivot =  drawing.PointF(*position)
    panel.Attributes.Bounds = drawing.RectangleF(0, 0, 30, 30)
    return panel

def create_group(doc, doc_objects):
    """ Creates and adds a grasshopper group to the document"""
    g = GH_Group() 
    g.Border = GH_GroupBorder.Blob
    g.Colour = drawing.Color.Azure
    [g.AddObject(doc_obj.InstanceGuid) for doc_obj in doc_objects]
    doc.AddObject(g, False)

The above two functions are used to create the text panels and group them respectively. Note that the properties attribute of the panel object is an instance of a GH_Properties class. The panel also owns an attribute object which is responsible for laying out and drawing the Panel. Nothing too interesting is going on with the Group object except for the call to ExpireCaches() on line 18 which is necessary for the group to be displayed.

def add_panels_to_doc(doc,positions, colours):
    panels = map(create_panel, positions, colours)
    panels[0].UserText = 'finished'
    group = gh.Kernel.Special.GH_Group()

    for i in range(1, len(panels)):
    for index, panel in enumerate(panels):
        doc.AddObject(panel, False, index)
    create_group(doc, panels)

This function adds panels to the document. There is another function that updates panels, but we won't cover that here. One interesting point is the use of a map function in line 2. We apply our create_panel function over a list of positions and colour parameters. map() was used extensively in a previous post about higher order functions. In the 1st for loop, we start to connect panels together (line 7). In the subsequent loop, these panels are  stored in the sticky dictionary and their ID is added to a newly created group. Once out of the loop, we call the create_group function.

if on:
    positions = [(p.X, p.Y) for p in pos]
    if len(positions) != len(sc.sticky['panels']):
        sc.sticky['panels'][:] = []
        add_panels_to_doc(doc, positions, col)
        update_panels(doc, positions, col)

if reset:
    toggle = ghenv.Component.Params.Input[0].Sources[0]
    toggle.Value = False
    sc.sticky['panels'][:] = []

So this is the last part of the script which deals with the response to the boolean toggle and button  inputs. When the toggle is set to True (on), we compare the length of the position input list and the number of panels stored in our sticky. If they do not match, we clear all existing panels and create new ones. Otherwise, we simply update the existing panels. When the button is pressed (reset), panels are cleared from the document. Another interesting event occurs in lines 11-12; we identify the toggle as a source and then switch it off. Therefore if we hit the reset button, it also turns the toggle to False.

I don't know how practical this example is but hopefully it provides you with an introduction to manipulating document objects. Here is the download link for the definition.

Definition for laying out components

Definition for laying out components