# -*- Perl -*-
# PostScript::Elements.pm
# An object for representing lines, circles, boxes, and images 
# such that they can be easily output as PostScript code.
#
# You may distribute this under the same terms as Perl
# itself.
#

package PostScript::Elements;
use strict;

use vars qw($VERSION);
$VERSION = '0.06';

# @paramnames is the list of valid parameters,
# %defaults is the hash of default values for those parameters.
#
my @paramnames = qw( type points linewidth linetint filltint );
my %defaults = (
                linewidth => 1,   # 1 pt  
	        filltint  => -1,  # -1 represents transparent
                linetint  => 0,   # 0 == black, 1 = white
               );

sub new {
    # The constructor method.
    # An Element object can be one or more lines, circles, 
    # boxes or images.
    #
    my $class = shift;
    my $self = [];       
    
    bless($self,$class);
    return $self;
}


sub addType {
    # A general routine for adding lines, circle, box, or
    # image objects to an Element.
    #
    my $self = shift;
    my %params = @_;
    
    # If a parameter is not specified, use the default...
    #
    foreach (@paramnames) {
        $params{$_} = $defaults{$_} unless (defined($params{$_}));
    };
 
    # Add the object to the current element
    #
    push @$self, { type      => $params{'type'},
                   points    => $params{'points'},
	           linewidth => $params{'linewidth'},
	           filltint  => $params{'filltint'},
                   linetint  => $params{'linetint'},
                 };
}

sub addArc {
    # Add an arc to the Element.
    # A points parameter is required, consisting of a reference to
    # list specifying the center coordinate, the radius 
    # of the arc, and two numbers specifying the starting angle 
    # and the ending angle describing the sweep of the arc. E.g.:
    # addArc(points=>[50,50,25,0,360]) 
    # would add a complete circle centered at 50,50 with a radius of 25.
    #
    my $self = shift;
    $self->addType(type => 'arc', 
                   @_);
}


sub addBox {
    # Add a Box to the Element.
    # The points parameter should consist of the upper left coordinate
    # of the box, its width, and its height.
    #
    my $self = shift;
    $self->addType(type => 'box', 
                   @_);
}


sub addLine {
    # Add a Line to the Element. 
    # The points parameter should contain the starting coordinate and
    # the end coordinate.
    #
    my $self = shift;
    $self->addType(type => 'line', 
                   @_);
}


sub Write() {
    # A method to create the PostScript code that will render the 
    # objects in the Element.
    #
    my $self = shift;
    my $returnval = "";
    my ($width, $height);    


    foreach my $element (@$self) {
        
	# Generate the appropriate PostScript based on the 
	# type attribute.
	# Arc:
        if ($element->{type} =~ /arc/i) {
	     # First save the current path (if any),
	     # then create the path of the arc
	     #
             $returnval .= "gsave\n".
                            $element->{'points'}->[0]." ".   # x value
                            $element->{'points'}->[1]." ".   # y value
	                    $element->{'points'}->[2]." ".   # radius
	                    $element->{'points'}->[3]." ".   # start angle 
	                    $element->{'points'}->[4]." ".   # end angle 
	                    "arc\n";
             # Don't fill the arc if filltint attribute is -1
	     # otherwise fill the current path with the tint specified by
	     # the filltint attribute. Save the path so it can be restored
	     # after the fill and it can be used by the stroke function.
	     #
             if ($element->{'filltint'} >= 0 ) {
	         $returnval .= $element->{'filltint'}." setgray\n". 
                               "gsave \nfill \ngrestore\n";
	     }
	     
	     # Stroke the arc with the width indicated by linewidth 
	     # and a greyscale percentage specified by linetint
	     #
	     $returnval .= $element->{'linetint'}." setgray\n".
	                   $element->{'linewidth'}." setlinewidth\n".
	                   "stroke\n".
                           "1 setgray\n".
	                   $defaults{'linewidth'}." setlinewidth\n".
                           "grestore\n";
       
        # Box:
	#
        } elsif ($element->{type} =~ /box/i) {
             # A box is described by the upper left corner, a width
             # and a height
             # 
             $width = $element->{'points'}->[2];
             $height = $element->{'points'}->[3];

             $returnval .= "gsave\n";
             $returnval .= $element->{'points'}->[0]." ".   # x value
                           $element->{'points'}->[1]." ".   # y value
                           "moveto\n".
                           # now draw it clockwise...
                           #
                           "$width 0 rlineto\n".
                           "0 ".(0-$height)." rlineto\n".
                           (0-$width)." 0 rlineto\n".
                           "0 $height rlineto\n".
                           "closepath\n";
             # Don't fill the box if filltint attribute is -1
	     #
             if ($element->{'filltint'} >= 0 ) {
	         $returnval .= $element->{'filltint'}." setgray\n". 
                               "gsave \nfill \ngrestore\n";
	     }
	     $returnval .= $element->{'linetint'}." setgray\n".
	                   $element->{'linewidth'}." setlinewidth\n".
	                   "stroke\n".
                           "1 setgray\n".
	                   $defaults{'linewidth'}." setlinewidth\n".
                           "grestore\n";

        # Line:
	#
        } elsif ($element->{type} =~ /line/i) {

             $returnval .= "gsave\n";
             $returnval .= $element->{'points'}->[0]." ".   # start x 
                           $element->{'points'}->[1]." ".   # start y
                           "moveto\n".
                           $element->{'points'}->[2]." ".   # end x
                           $element->{'points'}->[3]." ".   # end y
                           "lineto\n".
	                   $element->{'linetint'}." setgray\n".
	                   $element->{'linewidth'}." setlinewidth\n".
	                   "stroke\n".
                           "1 setgray\n".
	                   $defaults{'linewidth'}." setlinewidth\n".
                           "grestore\n";


        } else {
          # Do nothing
        }
    }
    return ($returnval);
}



1;

__END__


=head1 NAME

PostScript::Elements - Generate PostScript code for circles, boxes, lines

=head1 DESCRIPTION

An object for representing lines, circles, boxes, and images 
such that they can be easily output as PostScript code.

=head1 SYNOPSIS



=head1 AUTHOR



=cut

