GDGraph

 view release on metacpan or  search on metacpan

Graph/axestype.pm  view on Meta::CPAN

    x_last_label_skip	=> 0,

    # Do we want ticks on the x axis?
    x_ticks             => 1,
    x_all_ticks         => 0,

    # Where to place the x and y labels
    x_label_position    => 3/4,
    y_label_position    => 1/2,

    # vertical printing of x labels
    x_labels_vertical   => 0,
 
    # Draw axes as a box? (otherwise just left and bottom)
    box_axis            => 1,

    # Disable axes?
    # undef -> all axes, 0 -> Only line for bars, other -> no axes at all.
    no_axes             => undef,
 
    # Use two different axes for the first and second dataset. The first
    # will be displayed using the left axis, the second using the right
    # axis. You cannot use more than two datasets when this option is on.
    two_axes            => 0,

    # Which axis to use for each dataset. This only is in effect when
    # two_axes is true. The axis number will wrap around, just like
    # the dclrs array.
    use_axis            => [1, 2],
 
    # Print values on the axes?
    x_plot_values       => 1,
    y_plot_values       => 1,
 
    # Space between axis and text
    axis_space          => 4,
 
    # Do you want bars to be drawn on top of each other, or side by side?
    overwrite           => 0,

    # This will replace 'overwrite = 2'. For now, it is hardcoded to set
    # overwrite to 2
    cumulate            => 0,

    # Do you want me to correct the width of the graph, so that bars are
    # always drawn with a nice integer number of pixels?
    #
    # The GD::Graph::bars::initialise sub will switch this on.
    # Do not set this to anything else than undef!
    correct_width       => undef,

    # XXX The following two need to get better defaults. Maybe computed.
    # Draw the zero axis in the graph in case there are negative values
    zero_axis           =>  0,

    # Draw the zero axis, but do not draw the bottom axis, in case
    # box-axis == 0
    # This also moves the x axis labels to the zero axis
    zero_axis_only      =>  0,

    # Size of the legend markers
    legend_marker_height    => 8,
    legend_marker_width     => 12,
    legend_spacing          => 4,
    legend_placement        => 'BC',        # '[BR][LCR]'
    lg_cols                 => undef,

    # Display the y values above the bar or point in the graph.
    show_values             => undef,
    hide_overlapping_values => 0,
    values_vertical         => undef,   # vertical?
    values_space            => 4,       # extra spacing
    values_format           => undef,   # how to format the value
    
    # Draw the X axis left and the y1 axis at the bottom (y2 at top)
    rotate_chart            => undef,

    # CONTRIB Edwin Hildebrand
    # How narrow is a dataset allowed to become before we drop the
    # accents?
    accent_treshold         => 4,

    # Format of the numbers on the x and y axis
    y_number_format         => undef,
    y1_number_format        => undef,       # CONTRIB Andrew OBrien
    y2_number_format        => undef,       # CONTRIB Andrew OBrien
    x_number_format         => undef,       # CONTRIB Scott Prahl

    # and some attributes without default values
    x_label         => undef,
    y_label         => undef,
    y1_label        => undef,
    y2_label        => undef,
    x_min_value     => undef,
    x_max_value     => undef,
    y_min_value     => undef,
    y1_min_value    => undef,
    y2_min_value    => undef,
    y_max_value     => undef,
    y1_max_value    => undef,
    y2_max_value    => undef,
    y_min_range     => undef,               # CONTRIB Ben Tilly
    y1_min_range     => undef,
    y2_min_range     => undef,

    borderclrs      => undef,

    # XXX
    # Multiple inheritance (linespoints and mixed) finally bit me. The
    # _has_defaults and set methods can only work correctly when the
    # spot where the defaults are kept are in a mutual parent, which
    # would be this. The odd implementation of SUPER doesn't help

    # XXX points
    # The size of the marker to use in the points and linespoints graphs
    # in pixels
    marker_size => 4,

    # attributes with no default
    markers => undef,

    # XXX lines
    # The width of the line to use in the lines and linespoints graphs
    # in pixels
    line_width      => 1,

    # Set the scale of the line types
    line_type_scale => 8,

    # Which line types to use
    line_types      => [1],

    # Skip undefined values, and don't draw them at all
    skip_undef      => 0,

    # XXX bars
    # Spacing between the bars and groups of bars
    bar_width       => undef,
    bar_spacing     => 0,
    bargroup_spacing=> 0,                   # CONTRIB Grant McLean

    # cycle through colours per data point, not set
    cycle_clrs      => 0,

    # colour of the shadow
    shadowclr       => 'dgray',
    shadow_depth    => 0,

    # XXX mixed
    default_type    => 'lines',
    types           => undef,
);

sub _has_default { 
    my $self = shift;
    my $attr = shift || return;
    exists $Defaults{$attr} || $self->SUPER::_has_default($attr);
}

sub initialise
{
    my $self = shift;

    $self->SUPER::initialise();

    while (my($key, $val) = each %Defaults) 
        { $self->{$key} = $val }

    $self->set_x_label_font(GD::gdSmallFont);
    $self->set_y_label_font(GD::gdSmallFont);
    $self->set_x_axis_font(GD::gdTinyFont);
    $self->set_y_axis_font(GD::gdTinyFont);
    $self->set_legend_font(GD::gdTinyFont);
    $self->set_values_font(GD::gdTinyFont);
}

# PUBLIC
sub plot
{
    my $self = shift;
    my $data = shift;

    $self->check_data($data)            or return;
    $self->init_graph()                 or return;
    $self->setup_text()                 or return;
    $self->setup_legend();
    $self->setup_coords()               or return;
    $self->draw_text();
    unless (defined $self->{no_axes})
    {
        $self->draw_axes();
        $self->draw_ticks()             or return;
    }
    $self->draw_data()                  or return;
    $self->draw_values()                or return;
    $self->draw_legend();

    return $self->{graph}
}

sub set
{
    my $self = shift;
    my %args = @_;

    for (keys %args) 
    { 
        /^tick_length$/ and do 
        {
            $self->{x_tick_length} = 
            $self->{y_tick_length} = $args{$_};
            delete $args{$_};
            next;
        };
        /^long_ticks$/ and do 
        {
            $self->{x_long_ticks} = 
            $self->{y_long_ticks} = $args{$_};
            delete $args{$_};
            next;
        };
        /^overwrite$/ and do
        {
            $self->{cumulate} = 1 if $args{$_} == 2;
            $self->{overwrite} = $args{$_};
            delete $args{$_};
            next;
        };
        /^cumulate$/ and do
        {
            $self->{cumulate} = $args{$_};
            # XXX And for now
            $self->{overwrite} = 2 if $args{$_};
            delete $args{$_};
            next;
        };
    }

    return $self->SUPER::set(%args);
}

sub setup_text
{
    my $self = shift;

    $self->{gdta_x_label}->set(colour => $self->{lci});
    $self->{gdta_y_label}->set(colour => $self->{lci});
    $self->{xlfh} = $self->{gdta_x_label}->get('height');
    $self->{ylfh} = $self->{gdta_y_label}->get('height');

    $self->{gdta_x_axis}->set(colour => $self->{alci});
    $self->{gdta_y_axis}->set(colour => $self->{alci});
    $self->{xafh} = $self->{gdta_x_axis}->get('height');
    $self->{yafh} = $self->{gdta_x_axis}->get('height');

    $self->{gdta_title}->set(colour => $self->{tci});
    $self->{gdta_title}->set_align('top', 'center');
    $self->{tfh} = $self->{gdta_title}->get('height');

    $self->{gdta_legend}->set(colour => $self->{legendci});
    $self->{gdta_legend}->set_align('top', 'left');
    $self->{lgfh} = $self->{gdta_legend}->get('height');

    $self->{gdta_values}->set(colour => $self->{valuesci});
    unless ($self->{rotate_chart})
    {
        if ($self->{values_vertical})
        {
            $self->{gdta_values}->set_align('center', 'left');
        }
        else
        {
            $self->{gdta_values}->set_align('bottom', 'center');
        }
    }
    else
    {
        if ($self->{values_vertical})
        {
            $self->{gdta_values}->set_align('top', 'center');
        }
        else
        {
            $self->{gdta_values}->set_align('center', 'left');
        }
    }

    return $self;
}

sub set_x_label_font # (fontname)
{
    my $self = shift;
    $self->_set_font('gdta_x_label', @_);
}
sub set_y_label_font # (fontname)
{
    my $self = shift;
    $self->_set_font('gdta_y_label', @_);
}
sub set_x_axis_font # (fontname)
{
    my $self = shift;
    $self->_set_font('gdta_x_axis', @_);
}

sub set_y_axis_font # (fontname)
{
    my $self = shift;
    $self->_set_font('gdta_y_axis', @_);
}

sub set_values_font
{
    my $self = shift;
    $self->_set_font('gdta_values', @_);
}

sub set_legend # List of legend keys
{
    my $self = shift;
    $self->{legend} = [@_];
}

sub set_legend_font # (font name)
{
    my $self = shift;
    $self->_set_font('gdta_legend', @_);
}

sub get_hotspot
{
    my $self = shift;
    my $ds = shift;     # Which data set
    my $np = shift;     # Which data point?

    if (defined $np && defined $ds)
    {
        return @{$self->{_hotspots}->[$ds]->[$np]};
    }
    elsif (defined $ds)
    {
        return @{$self->{_hotspots}->[$ds]};
    }
    else
    {
        return @{$self->{_hotspots}};
    }
}

sub _set_feature_coords
{
    my $self = shift;
    my $feature = shift;
    my $type = shift;
    $self->{_feat_coords}->{$feature} = [ $type, @_ ];
}

sub _set_text_feature_coords
{
    my $self = shift;
    my $feature = shift;
    $self->_set_feature_coords($feature, "rect", @_[0,1,4,5]);
}

sub get_feature_coordinates
{
    my $self = shift;
    my $feature = shift;
    if ($feature)
    {
        $self->{_feat_coords}->{$feature};
    }
    else
    {
        $self->{_feat_coords};
    }
}

# PRIVATE

# inherit check_data from GD::Graph

#
# calculate the bottom of the bounding box for the graph
#
sub setup_bottom_boundary
{

Graph/axestype.pm  view on Meta::CPAN


    if ($self->{cumulate} && $ds > 1)
    {
        my $left;
        my $pvalue = $self->{_data}->get_y_cumulative($ds - 1, $np);
        ($left, $bottom) = $self->val_to_pixel($np + 1, $pvalue, $ds);
        $bottom = $left if $self->{rotate_chart};
    }

    return $bottom;
}

#
# Convert value coordinates to pixel coordinates on the canvas.
# TODO Clean up all the rotate_chart stuff
#
sub val_to_pixel    # ($x, $y, $dataset) or ($x, $y, -$axis) in real coords
{                   # return [x, y] in pixel coords
    my $self = shift;
    my ($x, $y, $i) = @_;

    # XXX use_axis
    my $axis = 1;
    if ( $self->{two_axes} ) {
        $axis = $i < 0 ? -$i : $self->{use_axis}[$i - 1];
    }
    
    my $y_min = $self->{y_min}[$axis];
    my $y_max = $self->{y_max}[$axis];
    my $y_range = ($y_max - $y_min) || 1; 
    # XXX the above might be an appropriate place for a conditional warning

    my $y_step = $self->{rotate_chart} ?
        abs(($self->{right} - $self->{left}) / $y_range) :
        abs(($self->{bottom} - $self->{top}) / $y_range);

    my $ret_x;
    my $origin = $self->{rotate_chart} ? $self->{top} : $self->{left};

    if (defined($self->{x_min_value}) && defined($self->{x_max_value}))
    {
        $ret_x = $origin + ($x - $self->{x_min}) * $self->{x_step};
    }
    else
    {
        $ret_x = ($self->{x_tick_number} ? $self->{x_offset} : $origin) 
            + $x * $self->{x_step};
    }
    my $ret_y = $self->{rotate_chart} ? 
        $self->{left} + ($y - $y_min) * $y_step :
        $self->{bottom} - ($y - $y_min) * $y_step;

    return $self->{rotate_chart} ?
        (_round($ret_y), _round($ret_x)) :
        (_round($ret_x), _round($ret_y));
}

#
# Legend
#
sub setup_legend
{
    my $self = shift;

    return unless defined $self->{legend};

    my $maxlen = 0;
    my $num = 0;

    # Save some variables
    $self->{r_margin_abs} = $self->{r_margin};
    $self->{b_margin_abs} = $self->{b_margin};

    foreach my $legend (@{$self->{legend}})
    {
        if (defined($legend) and $legend ne "")
        {
            $self->{gdta_legend}->set_text($legend);
            my $len = $self->{gdta_legend}->get('width');
            $maxlen = ($maxlen > $len) ? $maxlen : $len;
            $num++;
        }
        last if $num >= $self->{_data}->num_sets;
    }

    $self->{lg_num} = $num or return; 
    # not actually bug 20792 (unsure that this will ever get hit, but if it does..!)

    # calculate the height and width of each element
    my $legend_height = _max($self->{lgfh}, $self->{legend_marker_height});

    $self->{lg_el_width} = 
        $maxlen + $self->{legend_marker_width} + 3 * $self->{legend_spacing};
    $self->{lg_el_height} = $legend_height + 2 * $self->{legend_spacing};

    my ($lg_pos, $lg_align) = split(//, $self->{legend_placement});

    if ($lg_pos eq 'R')
    {
        # Always work in one column
        $self->{lg_cols} = 1;
        $self->{lg_rows} = $num;

        # Just for completeness, might use this in later versions
        $self->{lg_x_size} = $self->{lg_cols} * $self->{lg_el_width};
        $self->{lg_y_size} = $self->{lg_rows} * $self->{lg_el_height};

        # Adjust the right margin for the rest of the graph
        $self->{r_margin} += $self->{lg_x_size};

        # Set the x starting point
        $self->{lg_xs} = $self->{width} - $self->{r_margin};

        # Set the y starting point, depending on alignment
        if ($lg_align eq 'T')
        {
            $self->{lg_ys} = $self->{t_margin};
        }
        elsif ($lg_align eq 'B')
        {
            $self->{lg_ys} = $self->{height} - $self->{b_margin} - 
                $self->{lg_y_size};
        }
        else # default 'C'
        {
            my $height = $self->{height} - $self->{t_margin} - 
                $self->{b_margin};

            $self->{lg_ys} = 
                int($self->{t_margin} + $height/2 - $self->{lg_y_size}/2) ;
        }
    }
    else # 'B' is the default
    {
        # What width can we use
        my $width = $self->{width} - $self->{l_margin} - $self->{r_margin};

        (!defined($self->{lg_cols})) and 
            $self->{lg_cols} = int($width/$self->{lg_el_width}) || 1; # bug 20792
        
        $self->{lg_cols} = _min($self->{lg_cols}, $num);

        $self->{lg_rows} = 
            int($num / $self->{lg_cols}) + (($num % $self->{lg_cols}) ? 1 : 0);

        $self->{lg_x_size} = $self->{lg_cols} * $self->{lg_el_width};
        $self->{lg_y_size} = $self->{lg_rows} * $self->{lg_el_height};

        # Adjust the bottom margin for the rest of the graph
        $self->{b_margin} += $self->{lg_y_size};

        # Set the y starting point
        $self->{lg_ys} = $self->{height} - $self->{b_margin};

        # Set the x starting point, depending on alignment
        if ($lg_align eq 'R')
        {
            $self->{lg_xs} = $self->{width} - $self->{r_margin} - 
                $self->{lg_x_size};
        }
        elsif ($lg_align eq 'L')
        {
            $self->{lg_xs} = $self->{l_margin};
        }
        else # default 'C'
        {
            $self->{lg_xs} =  
                int($self->{l_margin} + $width/2 - $self->{lg_x_size}/2);
        }
    }
}

sub draw_legend
{
    my $self = shift;

    return unless defined $self->{legend};

    my $xl = $self->{lg_xs} + $self->{legend_spacing};
    my $y  = $self->{lg_ys} + $self->{legend_spacing} - 1;
    
    my $i = 0;
    my $row = 1;
    my $x = $xl;    # start position of current element

    foreach my $legend (@{$self->{legend}})
    {
        $i++;
        last if $i > $self->{_data}->num_sets;

        my $xe = $x;    # position within an element

        next unless defined($legend) && $legend ne "";

        $self->draw_legend_marker($i, $xe, $y);

        $xe += $self->{legend_marker_width} + $self->{legend_spacing};
        my $ys = int($y + $self->{lg_el_height}/2 - $self->{lgfh}/2);

        $self->{gdta_legend}->set_text($legend);
        $self->{gdta_legend}->draw($xe, $ys);

        $x += $self->{lg_el_width};

        if (++$row > $self->{lg_cols})
        {
            $row = 1;
            $y += $self->{lg_el_height};
            $x = $xl;
        }
    }
}

#
# This will be virtual; every sub class should define their own
# if this one doesn't suffice
#
sub draw_legend_marker # data_set_number, x, y
{
    my $s = shift;
    my $n = shift;
    my $x = shift;
    my $y = shift;

    my $g = $s->{graph};

    my $ci = $s->set_clr($s->pick_data_clr($n));
    return unless defined $ci;

    $y += int($s->{lg_el_height}/2 - $s->{legend_marker_height}/2);

    $g->filledRectangle(
        $x, $y, 
        $x + $s->{legend_marker_width}, $y + $s->{legend_marker_height},
        $ci
    );

    $g->rectangle(
        $x, $y, 
        $x + $s->{legend_marker_width}, $y + $s->{legend_marker_height},
        $s->{acci}
    );
}

"Just another true value";



( run in 1.608 second using v1.01-cache-2.11-cpan-e1769b4cff6 )