Example #1
0
def test_embed():
    p3.clear()
    x, y, z = np.random.random((3, 100))
    p3.scatter(x, y, z)
    p3.save("tmp/ipyolume_scatter_online.html", offline=False)
    assert os.path.getsize("tmp/ipyolume_scatter_online.html") > 0
    p3.save("tmp/ipyolume_scatter_offline.html",
            offline=True,
            scripts_path='js/subdir')
    assert os.path.getsize("tmp/ipyolume_scatter_offline.html") > 0
Example #2
0
    def plot(self,
             width=800,
             height=600,
             voxel_count_offset=0,
             voxel_limit=None,
             use_centroids_instead=False,
             ipyvol_fig=None,
             scaling=1,
             **kwargs):
        """
        This method needs better documentation.

        :param **kwargs: Is used in ipyvolume.pylab.scatter() or ipyvolume.pylab.plot() depending on whether use_centroids_instead
        is set to true or not.
        """
        if ipyvol_fig is None:
            p3.figure(width=width, height=height)
        else:
            p3.figure(ipyvol_fig)
        voxels_length = len(self)
        if voxel_count_offset >= voxels_length:
            raise ValueError(
                "voxel_count_offset is greater than the number of voxels!")
        else:
            if voxel_limit is None:
                n_voxels_to_plot = len(self)
            else:
                n_voxels_to_plot = voxel_count_offset + voxel_limit
                if n_voxels_to_plot > voxels_length:
                    n_voxels_to_plot = len(self)

        if use_centroids_instead:
            if 'marker' not in kwargs:
                kwargs['marker'] = 'sphere'
            if 'color' not in kwargs:
                kwargs['color'] = 'blue'
            if 'size' not in kwargs:
                kwargs['size'] = 0.5
            p3.scatter(
                *self.centroids[voxel_count_offset:n_voxels_to_plot].T *
                scaling, **kwargs)
        else:
            voxel_count_offset *= 18
            n_voxels_to_plot *= 18
            drawable_bboxes = self.drawable_bboxes[
                voxel_count_offset:n_voxels_to_plot]
            p3.plot(*drawable_bboxes.T * scaling, **kwargs)

        p3.squarelim()
        p3.show()
Example #3
0
def test_bokeh():
    from bokeh.io import output_notebook, show
    from bokeh.plotting import figure
    import ipyvolume.bokeh

    x, y, z = np.random.random((3, 100))

    p3.figure()
    scatter = p3.scatter(x, y, z)

    tools = "wheel_zoom,box_zoom,box_select,lasso_select,help,reset,"
    p = figure(title="E Lz space",
               tools=tools,
               webgl=True,
               width=500,
               height=500)
    r = p.circle(x, y, color="navy", alpha=0.2)
    ipyvolume.bokeh.link_data_source_selection_to_widget(
        r.data_source, scatter, 'selected')

    from bokeh.resources import CDN
    from bokeh.embed import components

    script, div = components(p)
    ipyvolume.embed.embed_html(
        "tmp/bokeh.html", [p3.gcc(), ipyvolume.bokeh.wmh],
        all=True,
        extra_script_head=script + CDN.render_js() + CDN.render_css(),
        body_pre="<h2>Do selections in 2d (bokeh)<h2>" + div +
        "<h2>And see the selection in ipyvolume<h2>")
Example #4
0
    def _scalar_handler(self, change):

        import ipyvolume.pylab as p3
        array_name = change['owner'].owner
        pa_widgets = self._widgets.particles[array_name]
        new = change['new']
        old = change['old']
        if old == 'None' and new == 'None':
            pass
        elif old != 'None' and new == 'None':
            self.scatters[array_name].visible = False
        elif old != 'None' and new != 'None':
            colormap = getattr(mpl.cm, pa_widgets.scalar_cmap.value)
            data = self.get_frame(self._widgets.frame.value)['arrays']
            c = colormap(getattr(data[array_name], pa_widgets.scalar.value))
            self.scatters[array_name].color = c
        else:
            colormap = getattr(mpl.cm, pa_widgets.scalar_cmap.value)
            data = self.get_frame(self._widgets.frame.value)['arrays']
            c = colormap(getattr(data[array_name], pa_widgets.scalar.value))
            self.scatters[array_name] = p3.scatter(
                data[array_name].x,
                data[array_name].y,
                data[array_name].z,
                color=c,
                size=pa_widgets.scalar_size.value,
            )
        self._legend_handler(None)
Example #5
0
def show_spatial_series(node: SpatialSeries, **kwargs):

    data, unit = get_timeseries_in_units(node)
    tt = get_timeseries_tt(node)

    if len(data.shape) == 1:
        fig, ax = plt.subplots()
        ax.plot(tt, data, **kwargs)
        ax.set_xlabel('t (sec)')
        if unit:
            ax.set_xlabel('x ({})'.format(unit))
        else:
            ax.set_xlabel('x')
        ax.set_ylabel('x')

    elif data.shape[1] == 2:
        fig, ax = plt.subplots()
        ax.plot(data[:, 0], data[:, 1], **kwargs)
        if unit:
            ax.set_xlabel('x ({})'.format(unit))
            ax.set_ylabel('y ({})'.format(unit))
        else:
            ax.set_xlabel('x')
            ax.set_ylabel('y')
        ax.axis('equal')

    elif data.shape[1] == 3:
        import ipyvolume.pylab as p3

        fig = p3.figure()
        p3.scatter(data[:, 0], data[:, 1], data[:, 2], **kwargs)
        p3.xlim(np.min(data[:, 0]), np.max(data[:, 0]))
        p3.ylim(np.min(data[:, 1]), np.max(data[:, 1]))
        p3.zlim(np.min(data[:, 2]), np.max(data[:, 2]))

    else:
        raise NotImplementedError

    return fig
Example #6
0
def init_vector(show=True):
    #This function initializes a figure with basis vectors in world frame - requires import ipyvolume.pylab as p3 outside scope
    #p3.clear()
    origin = np.array([0.0, 0.0, 0.0])
    x = origin + np.array([1.0, 0.0, 0.0])
    y = origin + np.array([0.0, 1.0, 0.0])
    z = origin + np.array([0.0, 0.0, 1.0])
    u = np.array([1.0, 0.0, 0.0])
    v = np.array([0.0, 1.0, 0.0])
    w = np.array([0.0, 0.0, 1.0])
    quiver = p3.quiver(x,
                       y,
                       z,
                       u,
                       v,
                       w,
                       size=10,
                       marker='arrow',
                       color=np.array([[1, 0, 0], [0, 1, 0],
                                       [0, 0, 1]]))  #ipv.show()
    scatter = p3.scatter(np.array([origin[0]]),
                         np.array([origin[0]]),
                         np.array([origin[0]]),
                         size=5,
                         marker='sphere',
                         color='red')
    line1 = p3.plot(np.array([origin[0], x[0]]),
                    np.array([origin[0], x[1]]),
                    np.array([origin[0], x[2]]),
                    color='red')
    line2 = p3.plot(np.array([origin[0], y[0]]),
                    np.array([origin[0], y[1]]),
                    np.array([origin[0], y[2]]),
                    color='green')
    line3 = p3.plot(np.array([origin[0], z[0]]),
                    np.array([origin[0], z[1]]),
                    np.array([origin[0], z[2]]),
                    color='blue')
    #scatter = p3.scatter(x,y,z,size=2,marker = 'sphere',color='red') # the origin
    if show:
        p3.show()
        p3.style.box_off()
    handle = {
        'origin': scatter,
        'arrows': quiver,
        'linex': line1,
        'liney': line2,
        'linez': line3
    }
    #p3.style.axes_off()
    return handle
Example #7
0
def test_widgets_state(performance):
    try:
        _remove_buffers = None
        try:
            from ipywidgets.widgets.widget import _remove_buffers
        except:
            pass
        ipyvolume.serialize.performance = performance
        x, y, z = np.random.random((3, 100))
        p3.figure()
        scatter = p3.scatter(x, y, z)
        state = scatter.get_state()
        if _remove_buffers:
            _remove_buffers(state)
        else:
            scatter._split_state_buffers(state)
    finally:
        ipyvolume.serialize.performance = 0
Example #8
0
    def interactive_plot(self):

        import ipyvolume.pylab as p3
        self._create_widgets()
        self.scatters = {}
        self.vectors = {}
        self.legend = widgets.Output()
        with self.legend:
            self.pltfigure = plt.figure(figsize=(8, 8))
            # creating a dummy figure, so that 'self.pltfigure.clf()'
            # in self._legend_handler() does not throw an error
            # during initialization

        p3.clear()
        data = self.get_frame(self._widgets.frame.value)['arrays']
        for array_name in self._widgets.particles.keys():
            pa_widgets = self._widgets.particles[array_name]
            if pa_widgets.scalar.value != 'None':
                colormap = getattr(mpl.cm, pa_widgets.scalar_cmap.value)
                c = colormap(getattr(data[array_name],
                                     pa_widgets.scalar.value))
                self.scatters[array_name] = p3.scatter(
                    data[array_name].x,
                    data[array_name].y,
                    data[array_name].z,
                    color=c,
                    size=pa_widgets.scalar_size.value,
                )
        self.plot_container = p3.gcc()
        self.plot = p3.gcf()  # used in 'self._save_figure_handler()'
        self._legend_handler(None)
        display(
            widgets.HBox(
                [widgets.VBox([self.plot]),
                 widgets.VBox([self.legend])]))
        # HBox does not allow custom layout, therefore using an HBox
        # of two VBoxes to place 'plot' and 'legend' next to each other
        display(self._widgets._create_vbox())
Example #9
0
    def interactive_plot(self):
        self._create_widgets()
        self.scatters = {}
        display(self._widgets._create_vbox())
        self.vectors = {}
        self.legend = widgets.Output()

        import ipyvolume.pylab as p3

        p3.clear()
        data = self.get_frame(self._widgets.frame.value)['arrays']
        for array_name in self._widgets.particles.keys():
            pa_widgets = self._widgets.particles[array_name]
            colormap = getattr(mpl.cm, pa_widgets.scalar_cmap.value)
            c = colormap(getattr(data[array_name], pa_widgets.scalar.value))
            self.scatters[array_name] = p3.scatter(
                data[array_name].x,
                data[array_name].y,
                data[array_name].z,
                color=c,
                size=pa_widgets.scalar_size.value,
            )
        self._legend_handler(None)
        display(widgets.VBox((p3.gcc(), self.legend)))
Example #10
0
def quickscatter(x, y, z, **kwargs):
    import ipyvolume.pylab as p3
    p3.figure()
    p3.scatter(x, y, z, **kwargs)
    return p3.current.container
Example #11
0
def test_scatter():
    x, y, z = np.random.random((3, 100))
    p3.scatter(x, y, z)
    p3.save("tmp/ipyolume_scatter.html")
def process_the_lidar(filename):
    # Get today's date, used for Nearmap data imagery. This way, the API will pull the latest image
    todays_date = datetime.datetime.today().strftime('%Y%m%d')

    trees = gpd.read_file(las_shapefile)

    # Name the output as the RBG plant id number
    lidarextension = filename.split('/')[-1].split('.')[0][-2:]
    lidarnumber = str(trees['RBGno'].loc[trees['LASfile'] == 'merged_0000' +
                                         str(lidarextension)].values[0])

    if os.path.isfile(
            os.path.join(os.getcwd(), output_folder, 'lidar',
                         lidarnumber + 'meta.csv')):
        print(lidarnumber, '- Already has been run, skipping')
    else:
        df = import_lidar(filename)
        df = df.loc[df['tree_potential']]
        print(lidarnumber, "- Amount of tree points in dataframe:", len(df))

        lep = local_max(
            df.loc[df['tree_potential'],
                   ['X', 'Y', 'Z', 'HeightAboveGround', 'X_0', 'Y_0', 'Z_0']],
            radius=3,
            density_threshold=15)
        treetop_spheres = p3.scatter(lep['Y'],
                                     lep['Z'],
                                     lep['X'],
                                     color='black',
                                     size=.5,
                                     marker='sphere')

        # Cluster the trees. We're using KDTree method here. But something better could be used
        kdtree = scipy.spatial.kdtree.KDTree(lep[['X', 'Y', 'Z']])
        dist, idx = kdtree.query(df.loc[df['tree_potential'],
                                        ['X', 'Y', 'Z']].values)
        df.loc[df['tree_potential'], 'tree_idx'] = idx

        # DBSCAN clustering alternative. This takes up too much memory when multiprocessing -- Python doesn't seem
        # garbage collect the lidar data and exceeds 32gb of RAM when using this. Although, will be ok when running
        # small LAS files individually. If used, comment out the last 3 lines and uncomment the next two.

        #dbscan = DBSCAN(eps=0.9, min_samples=7).fit(df.loc[df['tree_potential'],['X','Y','Z']].values)
        #df.loc[df['tree_potential'], 'tree_idx'] = dbscan.labels_

        # Define centroid of XYZ plane
        centroid = df['Y'].mean(), df['Z'].mean(), df['X'].mean()

        # Calculate distance of tree top points to centroid, add their distance to lep
        tree_distances = []
        for tree in lep.values.tolist():
            tree_distances.append(
                euclid_dist(tree[0], tree[1], centroid[0], centroid[1]))
        lep['distance'] = tree_distances

        # Pull out tree top with the shorted distance to centroid. The index will be the tree id
        closest_treetop = lep[lep['distance'] == lep['distance'].min()]
        # Builds an entire dataframe which corresponds to the tree that matched in the closest_treetop search
        centre_tree_df = df.loc[df['tree_idx'] == df.loc[
            closest_treetop.index[0]]['tree_idx']]

        # Get CRS of project from the shapefile of trees (caution: not the lidar)
        project_crs = trees.crs['init']
        nearmap_crs = 'epsg:4326'

        # Reproject coordinate of tree to one suitable for Nearmap's API (WGS84/EPSG:4326)
        inProj = Proj(init=project_crs)
        outProj = Proj(init=nearmap_crs)
        x1, y1 = closest_treetop[['X_0',
                                  'Y_0']].values[0][0], closest_treetop[[
                                      'X_0', 'Y_0'
                                  ]].values[0][1]
        x2, y2 = transform(inProj, outProj, x1, y1)

        # Tree's coordinates in WGS84
        tree_coords_wgs84 = (x2, y2)
        tree_coords_wgs84utm = (x1, y1)
        print(
            lidarnumber, "- WGS84 UTM coordinates are:\t",
            str(tree_coords_wgs84utm[1]) + ',' + str(tree_coords_wgs84utm[0]))
        print(lidarnumber, "- WGS84 long coordinates are:\t",
              str(tree_coords_wgs84[1]) + ',' + str(tree_coords_wgs84[0]))

        if not os.path.isdir(os.path.join('.', output_folder)):
            os.makedirs(os.path.join('.', output_folder))
            print(lidarnumber, "- Created folder",
                  os.path.join('.', output_folder))
        if not os.path.isdir(os.path.join('.', output_folder, 'lidar')):
            os.makedirs(os.path.join('.', output_folder, 'lidar'))
            print(lidarnumber, "- Created folder",
                  os.path.join('.', output_folder, 'lidar'))

        # Volume and height of tree cloud (in cubic metres, expecting coordinate system is metres - UTM WGS84 in this case)
        tree_volume = round(
            scipy.spatial.ConvexHull(centre_tree_df[['X', 'Y', 'Z']]).volume,
            2)
        tree_height = round(closest_treetop['HeightAboveGround'].values[0], 2)
        print(lidarnumber, "- Dimensions of the tree are:\t Volume:",
              str(tree_volume) + "m^3", "Height:",
              str(tree_height) + "m")

        # Save lidar 3D plot
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        ax.dist = 8
        ax.scatter(centre_tree_df['X'].values,
                   centre_tree_df['Y'].values,
                   centre_tree_df['Z'].values,
                   zdir="z",
                   s=1,
                   marker='^')
        ax.view_init(elev=10., azim=0)
        fig.savefig(os.path.join(os.getcwd(), output_folder, 'lidar',
                                 lidarnumber + '_plot0deg.jpg'),
                    dpi=400)
        print(
            lidarnumber, "- Saved 0° plot to:\t\t",
            os.path.join('.', output_folder, 'lidar',
                         lidarnumber + '_plot0deg.jpg'))
        ax.view_init(elev=10., azim=45)
        fig.savefig(os.path.join(os.getcwd(), output_folder, 'lidar',
                                 lidarnumber + '_plot45deg.jpg'),
                    dpi=400)
        print(
            lidarnumber, "- Saved 45° plot to:\t\t",
            os.path.join('.', output_folder, 'lidar',
                         lidarnumber + '_plot45deg.jpg'))
        ax.view_init(elev=10., azim=90)
        fig.savefig(os.path.join(os.getcwd(), output_folder, 'lidar',
                                 lidarnumber + '_plot90deg.jpg'),
                    dpi=400)
        print(
            lidarnumber, "- Saved 90° plot to:\t\t",
            os.path.join('.', output_folder, 'lidar',
                         lidarnumber + '_plot45deg.jpg'))
        plt.close()

        # Obtain nearmap imagery
        nearmap_url = "http://au0.nearmap.com/staticmap?center=" + str(
            tree_coords_wgs84[1]
        ) + "," + str(
            tree_coords_wgs84[0]
        ) + "&size=3000x3000&zoom=22&date=" + todays_date + "&httpauth=false&apikey=" + NEARMAP_API_KEY
        fn, _ = urllib.request.urlretrieve(
            nearmap_url,
            os.path.join(os.getcwd(), output_folder, 'lidar',
                         lidarnumber + '_aerial.jpg'))
        print(
            lidarnumber, "- Saved Nearmap imagery to:\t",
            os.path.join('.', output_folder, 'lidar',
                         lidarnumber + '_aerial.jpg'))

        import csv
        with open(
                os.path.join(os.getcwd(), output_folder, 'lidar',
                             lidarnumber + 'meta.csv'), 'w') as csvfile:
            lidar_metawriter = csv.writer(csvfile, delimiter=',')
            lidar_metawriter.writerow([
                'treenumber', 'long', 'lat', 'utm_x', 'utm_y', 'height',
                'tree_volume'
            ])
            lidar_metawriter.writerow([
                lidarnumber, tree_coords_wgs84[0], tree_coords_wgs84[1],
                tree_coords_wgs84utm[0], tree_coords_wgs84utm[1], tree_height,
                tree_volume
            ])
        print(
            lidarnumber, "- Written csv file: ./" + output_folder + "/lidar/" +
            lidarnumber + "meta.csv")
Example #13
0
def create_ivol(vstruct,
                width=500,
                height=400,
                ssize=5,
                min_voxels=None,
                max_voxels=None,
                **volargs):
    """

    Parameters
    ----------
    vstruct: dict
    width: int
    height: int
    ssize: int
    min_voxels : int
        minimum number of voxels in density cube
    max_voxels : int
        maximum number of voxels in density cube
    volargs: dict

    Returns
    -------

    Examples
    --------

    >>> from jsonextended import edict

    >>> dstruct = {
    ...  'type': 'repeat_density',
    ...  'dtype': 'charge',
    ...  'name': '',
    ...  'dcube':np.ones((3,3,3)),
    ...  'centre':[0,0,0],
    ...  'cell_vectors':{
    ...      'a':[2.,0,0],
    ...      'b':[0,2.,0],
    ...      'c':[0,0,2.]},
    ...   'color_bbox': 'black',
    ...   'transforms': []
    ... }
    >>> cstruct = {
    ...  'type': 'repeat_cell',
    ...  'name': '',
    ...  'centre':[0,0,0],
    ...  'cell_vectors':{
    ...      'a':[2.,0,0],
    ...      'b':[0,2.,0],
    ...      'c':[0,0,2.]},
    ...   'color_bbox': 'black',
    ...   'sites': [{
    ...         'label': "Fe",
    ...         'ccoord': [1,1,1],
    ...         'color_fill': "red",
    ...         'color_outline': None,
    ...         'transparency': 1,
    ...         'radius': 1,
    ...   }],
    ...   'bonds': [],
    ...   'transforms': []
    ... }
    >>> vstruct = {"elements": [dstruct, cstruct], "transforms": []}
    >>> new_struct, fig, controls = create_ivol(vstruct)

    >>> print(edict.apply(edict.filter_keys(new_struct, ["ivol"], list_of_dicts=True),
    ...                   "ivol", lambda x: [v.__class__.__name__ for v in x], list_of_dicts=True))
    {'elements': [{'ivol': ['Figure', 'Mesh']}, {'ivol': ['Mesh', 'Scatter']}]}

    """
    new_struct = apply_transforms(vstruct)
    bonds = compute_bonds(
        new_struct
    )  #edict.filter_keyvals(vstruct, {"type": "repeat_cell"}, keep_siblings=True))

    # ivolume currently only allows one volume rendering per plot
    # voltypes = edict.filter_keyvals(vstructs,[('type','repeat_density')])
    vol_index = [
        i for i, el in enumerate(vstruct['elements'])
        if el['type'] == 'repeat_density'
    ]
    assert len(
        vol_index) <= 1, "ipyvolume only allows one volume rendering per scene"

    p3.clear()
    fig = p3.figure(width=width, height=height, controls=True)
    fig.screen_capture_enabled = True

    # the volume rendering must be created first,
    # for appropriately scaled axis
    if vol_index:
        volstruct = new_struct['elements'][vol_index[0]]
        a = volstruct['cell_vectors']['a']
        b = volstruct['cell_vectors']['b']
        c = volstruct['cell_vectors']['c']
        centre = volstruct['centre']
        #print(centre)

        # convert dcube to cartesian
        out = cube_frac2cart(volstruct['dcube'],
                             a,
                             b,
                             c,
                             centre,
                             max_voxels=max_voxels,
                             min_voxels=min_voxels,
                             make_cubic=True)
        new_density, (xmin, ymin, zmin), (xmax, ymax, zmax) = out

        vol = p3.volshow(new_density, **volargs)

        if volstruct["color_bbox"] is not None:
            a = np.asarray(a)
            b = np.asarray(b)
            c = np.asarray(c)
            o = np.asarray(centre) - 0.5 * (a + b + c)
            mesh = _create_mesh([
                o, o + a, o + b, o + c, o + a + b, o + a + c, o + c + b,
                o + a + b + c
            ],
                                color=volstruct["color_bbox"],
                                line_indices=[[0, 1], [0, 2], [0, 3], [2, 4],
                                              [2, 6], [1, 4], [1, 5], [3, 5],
                                              [3, 6], [7, 6], [7, 4], [7, 5]])
            vol = [vol, mesh]

        # todo better way of storing ivol components?
        volstruct['ivol'] = vol

        # appropriately scale axis
        p3.xlim(xmin, xmax)
        p3.ylim(ymin, ymax)
        p3.zlim(zmin, zmax)

    for element in new_struct['elements']:
        if element['type'] == 'repeat_density':
            continue
        elif element['type'] == 'repeat_cell':
            scatters = []
            if element["color_bbox"] is not None:
                a = np.asarray(element['cell_vectors']['a'])
                b = np.asarray(element['cell_vectors']['b'])
                c = np.asarray(element['cell_vectors']['c'])
                centre = element['centre']
                o = np.asarray(centre) - 0.5 * (a + b + c)
                mesh = _create_mesh([
                    o, o + a, o + b, o + c, o + a + b, o + a + c, o + c + b,
                    o + a + b + c
                ],
                                    color=element["color_bbox"],
                                    line_indices=[[0, 1], [0, 2], [0,
                                                                   3], [2, 4],
                                                  [2, 6], [1, 4], [1,
                                                                   5], [3, 5],
                                                  [3, 6], [7, 6], [7, 4],
                                                  [7, 5]])
                scatters.append(mesh)

            for color, radius in set([(s['color_fill'], s['radius'])
                                      for s in element["sites"]]):

                scatter = edict.filter_keyvals(element, {
                    "color_fill": color,
                    "radius": radius
                },
                                               "AND",
                                               keep_siblings=True,
                                               list_of_dicts=True)
                scatter = edict.combine_lists(scatter, ["sites"],
                                              deepcopy=False)
                scatter = edict.remove_keys(scatter, ["sites"], deepcopy=False)

                x, y, z = np.array(scatter['ccoord']).T
                s = p3.scatter(x,
                               y,
                               z,
                               marker='sphere',
                               size=ssize * radius,
                               color=color)
                scatters.append(s)

            element['ivol'] = scatters
        elif element['type'] == 'repeat_poly':
            polys = []
            for poly in element['polys']:
                mesh = _create_mesh(poly, element['color'], element['solid'])
                polys.append(mesh)
            element['ivol'] = polys
        else:
            raise ValueError("unknown element type: {}".format(
                element['type']))

    for bond in bonds:
        p1, p2, c1, c2, radius = bond
        meshes = _create_bond(p1, p2, c1, c2, radius)

    # split up controls
    if vol_index:
        (level_ctrls, figbox, extractrl1, extractrl2) = p3.gcc().children
        controls = OrderedDict([('transfer function', [level_ctrls]),
                                ('lighting', [extractrl1, extractrl2])])
    else:
        # figbox = p3.gcc().children
        controls = {}

    return new_struct, fig, controls