The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
## @file
# Implementation of Chart::HorizontalBars
#
# maintained and written by the
# @author Chart Group at Geodetic Fundamental Station Wettzell (Chart@fs.wettzell.de)
# @date 2015-03-01
# @version 2.4.10

## @class Chart::HorizontalBars
# HorizontalBars class derived from class Base.
#
# This class provides all functions which are specific to
# horizontal bars
#
package Chart::HorizontalBars;

use Chart::Base '2.4.10';
use GD;
use Carp;
use strict;

@Chart::HorizontalBars::ISA     = qw(Chart::Base);
$Chart::HorizontalBars::VERSION = '2.4.10';

#>>>>>>>>>>>>>>>>>>>>>>>>>>#
#  public methods go here  #
#<<<<<<<<<<<<<<<<<<<<<<<<<<#

#>>>>>>>>>>>>>>>>>>>>>>>>>>>#
#  private methods go here  #
#<<<<<<<<<<<<<<<<<<<<<<<<<<<#

## @method private int _draw_x_ticks()
# draw the x-ticks and their labels
# Overwrites this function of Chart::Base
# @return status
#
sub _draw_x_ticks
{
    my $self      = shift;
    my $data      = $self->{'dataref'};
    my $font      = $self->{'tick_label_font'};
    my $textcolor = $self->_color_role_to_index('text');
    my $misccolor = $self->_color_role_to_index('misc');
    my ( $h, $w, $x1, $y1, $y2, $x2, $delta, $width, $label );
    my @labels = @{ $self->{'y_tick_labels'} };

    $self->{'grid_data'}->{'x'} = [];

    #make sure we have a real font
    unless ( ( ref $font ) eq 'GD::Font' )
    {
        croak "The tick label font you specified isn't a GD font object";
    }

    #get height and width of the font
    ( $h, $w ) = ( $font->height, $font->width );

    #get the right x-value and width
    if ( $self->{'y_axes'} =~ /^right$/i )
    {
        $x1 = $self->{'curr_x_min'};
        $width =
          $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - $self->{'text_space'} - $w * $self->{'x_tick_label_length'};
    }
    elsif ( $self->{'y_axes'} =~ /^both$/i )
    {
        $x1 = $self->{'curr_x_min'} + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'};
        $width =
          $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - $self->{'text_space'} - $w * $self->{'x_tick_label_length'};
    }
    else
    {
        $x1    = $self->{'curr_x_min'} + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'};
        $width = $self->{'curr_x_max'} - $x1;
    }

    #get the delta value
    $delta = $width / ( $self->{'y_ticks'} - 1 );

    #draw the labels
    $y2 = $y1;

    if ( $self->{'x_ticks'} =~ /^normal/i )
    {    #just normal ticks
            #get the point for updating later
        $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $h - $self->{'tick_len'};

        #get the start point
        $y2 = $y1 + $self->{'tick_len'} + $self->{'text_space'};
        for ( 0 .. $#labels )
        {
            $label = $self->{'y_tick_labels'}[$_];
            $x2 = $x1 + ( $delta * $_ ) - ( $w * length($label) / 2 );
            $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor );
        }
    }
    elsif ( $self->{'x_ticks'} =~ /^staggered/i )
    {    #staggered ticks
            #get the point for updating later
        $y1 = $self->{'curr_y_max'} - 3 * $self->{'text_space'} - 2 * $h - $self->{'tick_len'};

        for ( 0 .. $#labels )
        {
            $label = $self->{'y_tick_labels'}[$_];
            $x2 = $x1 + ( $delta * $_ ) - ( $w * length($label) / 2 );
            unless ( $_ % 2 )
            {
                $y2 = $y1 + $self->{'text_space'} + $self->{'tick_len'};
                $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor );
            }
            else
            {
                $y2 = $y1 + $h + 2 * $self->{'text_space'} + $self->{'tick_len'};
                $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor );
            }
        }

    }

    elsif ( $self->{'x_ticks'} =~ /^vertical/i )
    {    #vertical ticks
            #get the point for updating later
        $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $w * $self->{'y_tick_label_length'} - $self->{'tick_len'};

        for ( 0 .. $#labels )
        {
            $label = $self->{'y_tick_labels'}[$_];

            #get the start point
            $y2 = $y1 + $self->{'tick_len'} + $w * length($label) + $self->{'text_space'};

            $x2 = $x1 + ( $delta * $_ ) - ( $h / 2 );
            $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $label, $textcolor );
        }

    }

    else
    {
        carp "I don't understand the type of x-ticks you specified";
    }

    #update the curr x and y max value
    $self->{'curr_y_max'} = $y1;
    $self->{'curr_x_max'} = $x1 + $width;

    #draw the ticks
    $y1 = $self->{'curr_y_max'};
    $y2 = $self->{'curr_y_max'} + $self->{'tick_len'};
    for ( 0 .. $#labels )
    {
        $x2 = $x1 + ( $delta * $_ );
        $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor );
        if (   $self->true( $self->{'grid_lines'} )
            or $self->true( $self->{'x_grid_lines'} ) )
        {
            $self->{'grid_data'}->{'x'}->[$_] = $x2;
        }
    }

    return 1;
}

## @fn private int _draw_y_ticks()
#  draw the y-ticks and their labels
# Overwrites this function of Chart::Base
# @return status
sub _draw_y_ticks
{
    my $self      = shift;
    my $side      = shift || 'left';
    my $data      = $self->{'dataref'};
    my $font      = $self->{'tick_label_font'};
    my $textcolor = $self->_color_role_to_index('text');
    my $misccolor = $self->_color_role_to_index('misc');
    my ( $h, $w, $x1, $x2, $y1, $y2 );
    my ( $width, $height, $delta );

    $self->{'grid_data'}->{'y'} = [];

    #make sure that is a real font
    unless ( ( ref $font ) eq 'GD::Font' )
    {
        croak "The tick label font isn't a GD Font object!";
    }

    #get the size of the font
    ( $h, $w ) = ( $font->height, $font->width );

    #figure out, where to draw
    if ( $side =~ /^right$/i )
    {

        #get the right startposition
        $x1 = $self->{'curr_x_max'};
        $y1 = $self->{'curr_y_max'} - $h / 2;

        #get the delta values
        $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
        $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
        $y1 -= ( $delta / 2 );

        #look if skipping is desired
        if ( !defined( $self->{'skip_y_ticks'} ) )
        {
            $self->{'skip_y_ticks'} = 1;
        }

        #draw the labels
        for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} );
            $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'};
            $self->{'gd_obj'}
              ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor );
        }

        #draw the ticks
        $x1 = $self->{'curr_x_max'};
        $x2 = $self->{'curr_x_max'} + $self->{'tick_len'};
        $y1 += $h / 2;
        for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ( $delta * $_ );
            $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor );
            if (   $self->true( $self->{'grid_lines'} )
                or $self->true( $self->{'x_grid_lines'} ) )
            {
                $self->{'grid_data'}->{'y'}->[$_] = $y2;
            }
        }

    }
    elsif ( $side =~ /^both$/i )
    {

        #get the right startposition
        $x1 = $self->{'curr_x_max'};
        $y1 = $self->{'curr_y_max'} - $h / 2;

        #get the delta values
        $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
        $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
        $y1 -= ( $delta / 2 );

        #look if skipping is desired
        if ( !defined( $self->{'skip_y_ticks'} ) )
        {
            $self->{'skip_y_ticks'} = 1;
        }

        #first draw the right labels
        for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} );
            $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'};
            $self->{'gd_obj'}
              ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor );
        }

        #then draw the right ticks
        $x1 = $self->{'curr_x_max'};
        $x2 = $self->{'curr_x_max'} + $self->{'tick_len'};
        $y1 += $h / 2;
        for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ( $delta * $_ );
            $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor );
            if (   $self->true( $self->{'grid_lines'} )
                or $self->true( $self->{'x_grid_lines'} ) )
            {
                $self->{'grid_data'}->{'y'}->[$_] = $y2;
            }
        }

        #get the right startposition
        $x1 = $self->{'curr_x_min'};
        $y1 = $self->{'curr_y_max'} - $h / 2;

        #get the delta values for positioning
        $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
        $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
        $y1 -= ( $delta / 2 );

        #then draw the left labels
        for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} );
            $x2 =
              $x1 -
              $w * length( $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ) )    #print the Labels right-sided
              + $w * $self->{'x_tick_label_length'};
            $self->{'gd_obj'}
              ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor );
        }

        #update the curr_x_min val
        $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'};

        #finally draw the left ticks
        $x1 = $self->{'curr_x_min'};
        $x2 = $self->{'curr_x_min'} - $self->{'tick_len'};
        $y1 += $h / 2;
        for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ( $delta * $_ );
            $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor );
            if (   $self->true( $self->{'grid_lines'} )
                or $self->true( $self->{'x_grid_lines'} ) )
            {
                $self->{'grid_data'}->{'y'}->[$_] = $y2;
            }
        }
    }

    else
    {

        #get the right startposition
        $x1 = $self->{'curr_x_min'};
        $y1 = $self->{'curr_y_max'} - $h / 2;

        #get the delta values for positioning
        $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
        $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
        $y1 -= ( $delta / 2 );

        if ( !defined( $self->{'skip_y_ticks'} ) )
        {
            $self->{'skip_y_ticks'} = 1;
        }

        #draw the labels
        for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} );
            $x2 =
              $x1 -
              $w * length( $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ) )    #print the Labels right-sided
              + $w * $self->{'x_tick_label_length'};
            $self->{'gd_obj'}
              ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor );
        }

        #update the curr_x_min val
        $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'};

        #draw the ticks
        $x1 = $self->{'curr_x_min'};
        $x2 = $self->{'curr_x_min'} - $self->{'tick_len'};
        $y1 += $h / 2;
        for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) )
        {
            $y2 = $y1 - ( $delta * $_ );
            $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor );
            if (   $self->true( $self->{'grid_lines'} )
                or $self->true( $self->{'x_grid_lines'} ) )
            {
                $self->{'grid_data'}->{'y'}->[$_] = $y2;
            }
        }
    }

    #now return
    return 1;
}

## @fn private int _find_y_scale()
#  find good values for the minimum and maximum y-value on the chart
# overwrite the find_y_scale function, only to get the right f_x_ticks !!!!!
# @return status
sub _find_y_scale
{
    my $self = shift;

    # Predeclare vars.
    my ( $d_min,        $d_max );       # Dataset min & max.
    my ( $p_min,        $p_max );       # Plot min & max.
    my ( $tickInterval, $tickCount );
    my @tickLabels;                     # List of labels for each tick.
    my $maxtickLabelLen = 0;            # The length of the longest tick label.

    # Find the datatset minimum and maximum.
    ( $d_min, $d_max ) = $self->_find_y_range();

    # Force the inclusion of zero if the user has requested it.
    if ( $self->true( $self->{'include_zero'} ) )
    {
        if ( ( $d_min * $d_max ) > 0 )    # If both are non zero and of the same sign.
        {
            if ( $d_min > 0 )             # If the whole scale is positive.
            {
                $d_min = 0;
            }
            else                          # The scale is entirely negative.
            {
                $d_max = 0;
            }
        }
    }

    if ( $self->{'integer_ticks_only'} =~ /^\d$/ )
    {
        if ( $self->{'integer_ticks_only'} == 1 )
        {
            $self->{'integer_ticks_only'} = 'true';
        }
        else
        {
            $self->{'integer_ticks_only'} = 'false';
        }
    }
    if ( $self->true( $self->{'integer_ticks_only'} ) )
    {

        # Allow the dataset range to be overidden by the user.
        # f_min/max are booleans which indicate that the min & max should not be modified.
        my $f_min = defined $self->{'min_val'};
        $d_min = $self->{'min_val'} if $f_min;

        my $f_max = defined $self->{'max_val'};
        $d_max = $self->{'max_val'} if $f_max;

        # Assert against the min is larger than the max.
        if ( $d_min > $d_max )
        {
            croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)";
        }

        # The user asked for integer ticks, force the limits to integers.
        # & work out the range directly.
        $p_min = $self->_round2Tick( $d_min, 1, -1 );
        $p_max = $self->_round2Tick( $d_max, 1, 1 );

        my $skip = $self->{skip_int_ticks};

        $tickInterval = $skip;
        $tickCount    = ( $p_max - $p_min ) / $skip + 1;

        # Now sort out an array of tick labels.

        for ( my $labelNum = $p_min ; $labelNum <= $p_max ; $labelNum += $tickInterval )
        {
            my $labelText;

            if ( defined $self->{f_x_tick} )
            {

                # Is _default_f_tick function used?
                if ( $self->{f_x_tick} == \&_default_f_tick )
                {
                    $labelText = sprintf( "%d", $labelNum );
                }
                else
                {
                    $labelText = $self->{f_x_tick}->($labelNum);
                }
            }

            else
            {
                $labelText = sprintf( "%d", $labelNum );
            }

            #print "labelText = $labelText\n";
            push @tickLabels, $labelText;
            $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText;
        }

    }
    else
    {

        # Allow the dataset range to be overidden by the user.
        # f_min/max are booleans which indicate that the min & max should not be modified.
        my $f_min = defined $self->{'min_val'};
        $d_min = $self->{'min_val'} if $f_min;

        my $f_max = defined $self->{'max_val'};
        $d_max = $self->{'max_val'} if $f_max;

        # Assert against the min is larger than the max.
        if ( $d_min > $d_max )
        {
            croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)";
        }

        # Calculate the width of the dataset. (posibly modified by the user)
        my $d_width = $d_max - $d_min;

        # If the width of the range is zero, forcibly widen it
        # (to avoid division by zero errors elsewhere in the code).
        if ( 0 == $d_width )
        {
            $d_min--;
            $d_max++;
            $d_width = 2;
        }

        # Descale the range by converting the dataset width into
        # a floating point exponent & mantisa pair.
        my ( $rangeExponent, $rangeMantisa ) = $self->_sepFP($d_width);
        my $rangeMuliplier = 10**$rangeExponent;

        # Find what tick
        # to use & how many ticks to plot,
        # round the plot min & max to suatable round numbers.
        ( $tickInterval, $tickCount, $p_min, $p_max ) = $self->_calcTickInterval(
            $d_min / $rangeMuliplier,
            $d_max / $rangeMuliplier,
            $f_min, $f_max,
            $self->{'min_y_ticks'},
            $self->{'max_y_ticks'}
        );

        # Restore the tickInterval etc to the correct scale
        $_ *= $rangeMuliplier foreach ( $tickInterval, $p_min, $p_max );

        #get teh precision for the labels
        my $precision = $self->{'precision'};

        # Now sort out an array of tick labels.
        for ( my $labelNum = $p_min ; $labelNum <= $p_max ; $labelNum += $tickInterval )
        {
            my $labelText;

            if ( defined $self->{f_x_tick} )
            {

                # Is _default_f_tick function used?
                if ( $self->{f_x_tick} == \&_default_f_tick )
                {
                    $labelText = sprintf( "%." . $precision . "f", $labelNum );
                }
                else
                {
                    $labelText = $self->{f_x_tick}->($labelNum);
                }
            }
            else
            {
                $labelText = sprintf( "%." . $precision . "f", $labelNum );
            }

            #print "labelText = $labelText\n";
            push @tickLabels, $labelText;
            $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText;
        }
    }

    # Store the calculated data.
    $self->{'min_val'}             = $p_min;
    $self->{'max_val'}             = $p_max;
    $self->{'y_ticks'}             = $tickCount;
    $self->{'y_tick_labels'}       = \@tickLabels;
    $self->{'y_tick_label_length'} = $maxtickLabelLen;

    # and return.
    return 1;
}

## @fn private _draw_data
# finally get around to plotting the data for (horizontal) bars
sub _draw_data
{
    my $self      = shift;
    my $data      = $self->{'dataref'};
    my $misccolor = $self->_color_role_to_index('misc');
    my ( $x1, $x2, $x3, $y1, $y2, $y3 );
    my $cut = 0;
    my ( $width, $height, $delta1, $delta2, $map, $mod, $pink );
    my ( $i, $j, $color );

    # init the imagemap data field if they wanted it
    if ( $self->true( $self->{'imagemap'} ) )
    {
        $self->{'imagemap_data'} = [];
    }

    # find both delta values ($delta1 for stepping between different
    # datapoint names, $delta2 for setpping between datasets for that
    # point) and the mapping constant
    $width  = $self->{'curr_x_max'} - $self->{'curr_x_min'};
    $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
    $delta1 = $height / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
    $map    = $width / ( $self->{'max_val'} - $self->{'min_val'} );
    if ( $self->true( $self->{'spaced_bars'} ) )
    {
        $delta2 = $delta1 / ( $self->{'num_datasets'} + 2 );
    }
    else
    {
        $delta2 = $delta1 / $self->{'num_datasets'};
    }

    # get the base x-y values
    $y1 = $self->{'curr_y_max'} - $delta2;
    if ( $self->{'min_val'} >= 0 )
    {
        $x1  = $self->{'curr_x_min'};
        $mod = $self->{'min_val'};
    }
    elsif ( $self->{'max_val'} <= 0 )
    {
        $x1  = $self->{'curr_x_max'};
        $mod = $self->{'max_val'};
    }
    else
    {
        $x1  = $self->{'curr_x_min'} + abs( $map * $self->{'min_val'} );
        $mod = 0;
        $self->{'gd_obj'}->line( $x1, $self->{'curr_y_min'}, $x1, $self->{'curr_y_max'}, $misccolor );
    }

    # draw the bars
    for $i ( 1 .. $self->{'num_datasets'} )
    {

        # get the color for this dataset
        $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) );

        # draw every bar for this dataset
        for $j ( 0 .. $self->{'num_datapoints'} )
        {

            # don't try to draw anything if there's no data
            if ( defined( $data->[$i][$j] ) )
            {

                # find the bounds of the rectangle
                if ( $self->true( $self->{'spaced_bars'} ) )
                {
                    $y2 = $y1 - ( $j * $delta1 ) - ( $self->{'num_datasets'} * $delta2 ) + ( ( $i - 1 ) * $delta2 );
                }
                else
                {
                    $y2 = $y1 - ( $j * $delta1 ) - ( $self->{'num_datasets'} * $delta2 ) + ( ($i) * $delta2 );
                }
                $x2 = $x1;
                $y3 = $y2 + $delta2;

                #cut the bars off, if needed
                if ( $data->[$i][$j] > $self->{'max_val'} )
                {
                    $x3 = $x1 + ( ( $self->{'max_val'} - $mod ) * $map ) - 1;
                    $cut = 1;
                }
                elsif ( $data->[$i][$j] < $self->{'min_val'} )
                {
                    $x3 = $x1 + ( ( $self->{'min_val'} - $mod ) * $map ) + 1;
                    $cut = 1;
                }
                else
                {
                    $x3 = $x1 + ( ( $data->[$i][$j] - $mod ) * $map );
                    $cut = 0;
                }

                # draw the bar
                ## y2 and y3 are reversed in some cases because GD's fill
                ## algorithm is lame
                if ( $data->[$i][$j] < 0 )
                {
                    $self->{'gd_obj'}->filledRectangle( $x3, $y2, $x2, $y3, $color );
                    if ( $self->true( $self->{'imagemap'} ) )
                    {
                        $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y2, $x2, $y3 ];
                    }

                    $self->{'gd_obj'}->filledRectangle( $x3, $y2, $x2, $y3, $color );
                    if ( $self->true( $self->{'imagemap'} ) )
                    {
                        $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y2, $x2, $y3 ];
                    }
                }
                else
                {
                    $self->{'gd_obj'}->filledRectangle( $x2, $y2, $x3, $y3, $color );
                    if ( $self->true( $self->{'imagemap'} ) )
                    {
                        $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2, $x3, $y3 ];
                    }
                }

                # now outline it. outline red if the bar had been cut off
                unless ($cut)
                {
                    $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $misccolor );
                }
                else
                {
                    $pink = $self->{'gd_obj'}->colorAllocate( 255, 0, 255 );
                    $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $pink );
                }

            }
            else
            {
                if ( $self->true( $self->{'imagemap'} ) )
                {
                    $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef(), undef(), undef() ];
                }
            }
        }
    }

    # and finaly box it off
    $self->{'gd_obj'}
      ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
    return;

}

## be a good module and return 1
1;