# Adjust cropping extents example

A custom dialog to adjust the parameters of a GeometryFilter to crop data points.

This example shows how to use a GeometryFilter to crop data points, but also how to build a custom dialog to easily set interactively parameters of a filter, or any other Mayavi object.

The GeometryFilter crops all data within a bounding box specified by ‘extents’. In this example, we want to be able to tweak these extents interactively. For this, we build a Traits object that has ‘x_min’, ‘x_max’, ‘y_min’, … attributes. Traits enables us to represent this object as a dialog box. We use a callback called when these attributes are modified to propagate them to the filter. For more information on creating GUIs with Traits:

> http://code.enthought.com/projects/traits/docs/html/tutorials/traits_ui_scientific_app.html

> http://code.enthought.com/projects/traits/documentation.php

Python source code: `adjust_cropping_extents.py`

    """
    # Author: Gael Varoquaux <gael.varoquaux@normalesup.org>
    # Copyright (c) 2010, Enthought, Inc.
    # License: BSD Style.

    ################################################################################
    # Create a set of points, with given density
    import numpy as np
    x, y, z, s = np.random.random((4, 500))

    ################################################################################
    # A dialog to edit a range interactively and propagate it to the filter
    from traits.api import HasTraits, Range, Float, Instance, \
        on_trait_change
    from traitsui.api import View

    class ExtentDialog(HasTraits):
        """ A dialog to graphical adjust the extents of a filter.
        """

        # Data extents
        data_x_min = Float
        data_x_max = Float
        data_y_min = Float
        data_y_max = Float
        data_z_min = Float
        data_z_max = Float

        x_min = Range('data_x_min', 'data_x_max', 'data_x_min')
        x_max = Range('data_x_min', 'data_x_max', 'data_x_max')
        y_min = Range('data_y_min', 'data_y_max', 'data_y_min')
        y_max = Range('data_y_min', 'data_y_max', 'data_y_max')
        z_min = Range('data_z_min', 'data_z_max', 'data_z_min')
        z_max = Range('data_z_min', 'data_z_max', 'data_z_max')

        filter = Instance(HasTraits, allow_none=False)

        @on_trait_change('x_min,x_max,y_min,y_max,z_min,z_max')
        def update_extent(self):
            if (self.filter is not None
                        and self.x_min < self.x_max
                        and self.y_min < self.y_max
                        and self.z_min < self.z_max
                                ):
                self.filter.extent = (self.x_min, self.x_max,
                                      self.y_min, self.y_max,
                                      self.z_min, self.z_max)

        view = View('x_min', 'x_max', 'y_min', 'y_max', 'z_min', 'z_max',
                    title='Edit extent', resizable=True)

    ################################################################################
    # Now build the visualization using mlab
    from mayavi import mlab
    fig = mlab.figure(1, bgcolor=(1, 1, 1))

    # Create unconnected points
    pts = mlab.pipeline.scalar_scatter(x, y, z, s)
    mlab.outline(pts)

    # Use a geometry_filter to filter with a bounding box
    geometry_filter = mlab.pipeline.user_defined(pts,
                                       filter='GeometryFilter')
    geometry_filter.filter.extent_clipping = True
    # Connect our dialog to the filter
    extent_dialog = ExtentDialog(
                data_x_min=0, data_x_max=1,
                data_y_min=0, data_y_max=1,
                data_z_min=0, data_z_max=1,
                filter=geometry_filter.filter)
    # We need to use 'edit_traits' and not 'configure_traits()' as we do
    # not want to start the GUI event loop (the call to mlab.show())
    # at the end of the script will do it.
    extent_dialog.edit_traits()

    # The geometry_filter leaves hanging points, we need to add a
    # CleanPolyData filter to get rid of these.
    clip = mlab.pipeline.user_defined(geometry_filter,
                                        filter='CleanPolyData')

    # Finally, visualize the remaining points with spheres using a glyph
    # module
    spheres = mlab.pipeline.glyph(clip, scale_factor=.1)

    mlab.show()