The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Data::Region - define hierarchical areas with behaviors

SYNOPSIS

    use Data::Region;
    
    $r = Data::Region->new( 8.5, 11, { data => PageObj->new() } );
    $r->data( PageObj->new() );
    
    foreach my $c ( $r->subdivide(2.5,3) ) {
      $a = $c->area(0.25,0.25, 2.25,2.75);
      $a2 = $c->area(0.25,0.25, -0.25,-0.25); # as offset from lower right
    
      ($t,$m,$b) = $a->split_vertical(2,5,1);     # sequential heights
      ($t,$m,$b) = $a->split_vertical_abs(0,2,7); # absolute offsets
      ($l,$r) = $a->split_horizontal(2); # $l gets width of 2, $r gets the rest
    
      my($x1,$y1,$x2,$y2) = $a->coords();
      my $data = $a->data(); # data inherits from parent, if not set
      $a->action( sub { $data->setfont("Times-Bold", 10);
                        $data->text($x1,$y1, "Some Text");
                        $data->line( $_[0]->coords() ); # the non-closure way
                      } );
    }
    $r->render(); # heirarchically perform all the actions
    
    # Get some info about a region:
    ($w,$h) = ( $a->width(), $a->height() );
    ($x1,$y1, $x2,$y2) = $a->coords();
    ($x1,$y1) = $a->top_left();
    ($x2,$y1) = $a->top_right();
    ($x1,$y2) = $a->bottom_left();
    ($x2,$y2) = $a->bottom_right();

DESCRIPTION

Data::Region allows you to easily define a set of nested (2-dimensional) areas, defined by related coordinates, and to associate actions with them. The actions can then be performed hierarchically from any root of the tree.

Data::Region was written to provide an easy way to do simple page layout, but has, perhaps, more general uses.

USAGE

Creating Data::Regions

The following methods allow you to create Data::Regions, and Data::Regions within Data::Regions (from whence it's turtles all the way down).

new( $width, $height, [ \%options ] )

Creates a new (root) Data::Region object with the given width and height. The coordinates of this Data::Region are screen-oriented, so they range from (0,0) in the top left corner to ($width,$height) in the lower right corner.

The final argument, if given, is a hashref of options. Currently the only supported option is 'data'.

data

This option takes a reference to a data object. This Data::Region and all regions creted under it have access to this reference via the data() method. Child Data::Regions created under this one automatically inherit this reference as their own 'data', but can override it for themselves if desired.

$r->area( $topleft_x, $topleft_y, $botright_x, $botright_y )

Creates a sub-region of Data::Region $r. The corner coordinates of this area are relative to $r's coordinates, so for example

  $r->area( 1,1, 2,2 );

creates an area whose top left coordinate is offset (1,1) from the top left coordinate of $r, and whose bottom right coordinate is offset (2,2), also from $r's top left corner. In this case, if the corners of $r were (5,5)-(10,10), the created area would have corners (6,6)-(7,7).

If you specify (either or both coordinates of) the bottom right corner as negative numbers, the area's bottom right corner will be offset from the parent Data::Region's bottom right corner. For example

  $r->area( 1,1, -1,-1 );

Given the same parent region as the previous example, this would create an area with corners (6,6)-(9,9).

area() returns a new Data::Region object.

$r->subdivide( $width, $height )

Tiles the parent Data::Region into areas of the given dimensions. subdivide() will try to fit as many in as possibly, but will return only whole areas and so the returned areas might not cover the entire parent region. The first area created will be in the top left-most corner of the parent region, and proceeds in a left-to-right, top-to-bottom manner while space remains.

For example, if $r has width of 8.5 and height of 11, this

  @a = $r->subdivide( 2.5, 3 );

returns 9 new Data::Regions into @a (int(8.5/2.5)=3, int(11/3)=3, 3*3=9).

subdivide() returns a list of new Data::Region objects.

$r->split_vertical( $height1, $height2, $height3, ... )

Creates a set of child areas tiled vertically into the parent Data::Region. Each successive area is created with a height corresponding to the given argument, beginning at the top of the parent region and proceeding downwards. If the given heights do not fill the entire area of the parent region, a final area is returned which covers the remaining space. The list of heights may proceed beyond the bottom of the parent region.

For example, if $r has corners at (5,5)-(10,10), this

  ($a,$b,$c,$d) = $r->split_vertical( 2, 0.5, 1 );

creates the following: $a is (5,5)-(10,7), $b is (5,7)-(10,7.5), $c is (5,7.5)-(10,8.5), and $d gets all the rest (5,8.5)-(10,10).

split_vertical() returns a list of new Data::Region objects.

$r->split_vertical_abs( @offsets )

Similar to split_vertical(), but the arguments specify Y-coordinates of successive top-left corners, instead of heights (relative to the parent Data::Region). The first argument does not need to be 0.

split_vertical_abs() returns a list of new Data::Region objects.

$r->split_horizontal( $width1, $width2, $width3, ... )

Similar to split_vertical(), except tiled child areas are created left-to-right, instead of top-to-bottom.

split_horizontal() returns a list of new Data::Region objects.

$r->split_horizontal_abs( @offsets )

To split_horizontal() as split_vertical_abs() is to split_vertical().

split_horizontal_abs() returns a list of new Data::Region objects.

Finding out about Data::Regions

The following methods allow you to query Data::Region objects for data about the areas they represent.

$r->coords()

Returns the coordinates of the corners of this Data::Region. The list returned is (top-left-X, top-left-Y, bottom-right-X, bottom-right-Y).

$r->width()

Returns the width of this Data::Region (the difference between the Y coordinates of the top-left and bottom-right corners).

$r->height()

Returns the height of this Data::Region (the difference between the X coordinates of the top-left and bottom-right corners).

$r->top_left()

Returns a list of the X and Y coordinates for the top left corner of this Data::Region.

$r->top_right

Returns a list of the X and Y coordinates for the top right corner of this Data::Region.

$r->bottom_right

Returns a list of the X and Y coordinates for the bottom right corner of this Data::Region.

$r->bottom_left

Returns a list of the X and Y coordinates for the bottom left corner of this Data::Region.

Associating behavior with Data::Regions

The following methods allow you to associate data and callbacks to a tree of Data::Regions, and to request a Data::Region to perform its tree of callbacks.

$r->data( [$reference] )

Returns, and optionally sets, the data reference associated with this Data::Region. Any child regions created under this Data::Region inherit the reference of their parent (at the time they are created).

data() returns the a reference for the current 'data' field.

$r->action( $coderef )

Associates an action with this Data::Region, to be performed when this Data::Region's, or an ancestor Data::Region of this Data::Region's, render() method is called.

The argument is a code reference, which will be called with the Data::Region object as its parameter. So for example, you may do

  $r->action( sub {
                my $self = shift;
                # ...$self is the same obj as $r when this is run
              } );

A Data::Region may have any number of actions. Actions will be executed in the order that they were assocaited with action().

$r->render()

Performs the actions associated with this Data::Region, and all of its child Data::Regions.

Actions are performed for this region first, then for all of its children recursively. For each Data::Region, actions are performed in the order that they were added to that Data::Region. The order in which child Data::Regions are recursed into is undefined, but is probably the same as the order they were created in (eg, that's the way it currently works, but is subject to change).

INTERNAL METHODS

The following should be used only within the module itself.

$r->_init( @args )

Performs object initialization. Called by new(), the purpose of this method is to separate initialization logic from object-creation gruntwork. If you subclass this module, it should be sufficient to override _init() rather than new().

The list of arguments passed to new() are provided.

_init() returns nothing.

$r->_spawn( $x,$y, $width, $height )

Creates a child Data::Region inside $r, using the given absolute coordinates and dimensions. This method handles maintenence of the parent's list of children, and the new children's attribute inheritance from the parent. _spawn() is used by all the Data::Region creation methods (except new()).

_spawn() returns a new Data::Region object.

AUTHOR

Greg Fast <gdf@speakeasy.net>

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.