#!/usr/bin/perl
############################################################
## graphplotter
## Written By: Rohit Mishra ( rohit[at]rohitmishra.com)
############################################################

use strict;
use Tk 800;
use Math::Trig;

my $VERSION = "1.04";
my $ver='1.04';
my $bg_color='wheat2';
my $current_scale = 10;

my $PI = 3.1415927;

my $mw = MainWindow->new(-title=>"Graph $ver", -background=>$bg_color);
$mw->minsize(qw/900 600/ );

my $menubar = $mw->Frame(-relief => 'raised', -borderwidth => 2)->form(-top=>'%0',-left=>'%0',-right=>'%100');

$menubar->Menubutton(qw/-text File -underline 0 -tearoff 0 -menuitems/ =>
  [
    [Button => 'Function',    -command => \&Function],
    [Button => 'Refresh',    -command => \&Refresh],
    [Separator => ''],
    [Button => 'Quit', -command => sub{exit}],
  ])->pack(-side=>'left');

my $canvas = $mw->Canvas( -confine => 'yes', -cursor=> 'crosshair', -background => $bg_color );

my $xscroll = $mw->Scrollbar(-orient => 'horizontal',-command => ['xview', $canvas]);
my $yscroll = $mw->Scrollbar(-orient => 'vertical',-command => ['yview', $canvas]);

my $bottom_menu = $mw->Frame( -relief => 'raised', -borderwidth => 2);

$bottom_menu->form( -left=>'%0',-right=>'%100', -bottom=>'%100');
$canvas->form( -top=>$menubar,-left=>'%0',-right=>$yscroll, -bottom=> $xscroll );
$yscroll->form( -top => $menubar, -bottom => $bottom_menu, -right=>'%100');
$xscroll->form( -left => '%0', -right => $yscroll, -bottom=> $bottom_menu );


$canvas->configure(-xscrollcommand => ['set', $xscroll]);
$canvas->configure(-yscrollcommand => ['set', $yscroll]);
$canvas->configure(-scrollregion => [ 0,0,900,600 ] );


####my $canvas = $mw->Scrolled('Canvas', -cursor=> 'crosshair', -background => $bg_color, -scrollbars => 'se' )->form(-top=>$menubar,-left=>'%0',-right=>'%100');



my $zoomin = $bottom_menu->Button(-text=>"Zoom In", -relief=> 'raised', -command=>[ sub{ &Zoom_In;} ])->pack(-side => 'left');
my $zoomout = $bottom_menu->Button(-text=>"Zoom Out", -relief=> 'raised', -command=>[ sub{ &Zoom_Out;} ])->pack(-side => 'left');
my $fullview = $bottom_menu->Button(-text=>"Full View", -relief=> 'raised', -command=>[ sub{ &Full_View;} ])->pack(-side => 'left');

$mw->bind( '<ButtonPress-1>' => [ sub{ &Scale;} ]);
$mw->bind( '<ButtonRelease-1>' => [ sub{ &Scale_Del;} ]);
$canvas->bind( 'all', '<ButtonPress-1>' => [ sub{ &Select_Curve;} ] );

&Coordinates();
MainLoop;

sub Refresh(){
	$canvas->delete('all');
	&Coordinates();
}

sub Scale_Del(){
	$canvas->delete('coord_bar');
	$canvas->configure( -cursor => 'crosshair' );
	#$canvas->scanDragto($Tk::event->x, $Tk::event->y);
}

sub Scale(){
	my $xo = $Tk::event->x;
        my $yo = $Tk::event->y;
	my $text = "$xo,$yo";
	$canvas->createText( $xo, $yo, -text => $text, -tags => 'coord_bar' );
}

sub Scale1(){
	my $xo = $Tk::event->x;
        my $yo = $Tk::event->y;
        my @pt = &Transform($xo,$yo);
        $pt[0] = $pt[0]/$current_scale;
        $pt[1] = $pt[1]/$current_scale;
        my $text = "$xo,$yo";
        #my $text = "$pt[0],$pt[1]";
	$canvas->configure( -cursor => 'plus' );
        $canvas->createText( $xo, $yo, -text => $text, -tags => 'coord_bar' );
        #$canvas->createLine( $xo, 0, $xo, 900, -tags => 'coord_bar' );
        #$canvas->createLine( 0, $yo, 900, $yo, -tags => 'coord_bar' );
	#$canvas->scanMark($xo,$yo); 
}

sub Transform(){
	my @coordinates = @_;
	my @transformed = ();
	$transformed[0] = $coordinates[0] - 140;
	$transformed[1] = -($coordinates[1] - 140);
	return( @transformed );
}

sub Reverse_Transform(){
	my @coordinates = @_;
        my @transformed = ();
        $transformed[0] = ($coordinates[0] + 140);
        $transformed[1] = -($coordinates[1] - 140);
        return( @transformed );
}

sub Function () {
	my $curvetag = 'curve';
	my $top = $mw->Toplevel( -title => "Enter Function" );
	my $entryframe = $top->Frame( -relief => 'raised', -borderwidth => 2)->form( -top => '%0',-left=>'%0',-right=>'%100');
	my $func_label = $entryframe->Label( -text => "Enter polynomial as a function of x" )->pack;
	my $func_entry = $entryframe->Entry( -relief => 'sunken')->pack( -fill => 'x' );
	my $range_label = $entryframe->Label( -text => "Enter range of x" )->pack;
	my $range_entry = $entryframe->Entry( -relief => 'sunken')->pack( -fill => 'x' );
	my $buttonframe = $top->Frame( -relief => 'raised', -borderwidth => 2)->form( -top => $entryframe, -left=>'%0',-right=>'%100', -bottom=>'%100');
	my $plot = $buttonframe->Button(-text=>"Plot", -relief=> 'raised', -command=>[ sub{ 
									&Plot_Function( $func_entry, $range_entry, $curvetag ); 
									$curvetag++;	
									}])->pack( -side => 'left');
	my $close = $buttonframe->Button(-text=>"Close", -relief=> 'raised', -command=>[ sub{ $top->destroy;} ] )->pack( -side => 'left');
}


sub Coordinates (){
	my @xaxis_left = &Reverse_Transform( -3000, 0 );
	my @xaxis_right = &Reverse_Transform( 3000, 0 );
	my @yaxis_top = &Reverse_Transform( 0, 3000 );
	my @yaxis_bot = &Reverse_Transform( 0, -3000 );

	$canvas->createLine( $xaxis_left[0], $xaxis_left[1], $xaxis_right[0], $xaxis_right[1], -width => '1.0', -tags => 'Coords');
	$canvas->createLine( $yaxis_top[0], $yaxis_top[1], $yaxis_bot[0], $yaxis_bot[1] , -width => '1.0', -tags => 'Coords');
}

sub Plot_Function (){
	my $entry1 = shift;
	my $entry2 = shift;
	my $curvetag = shift;
	my $function = $entry1->get;
	my $range = $entry2->get;
	print "function x       {", $function , "}\n" ;
	print "function x range {", $range , "}\n" ;
	my $x;
	my $y;
	my $delta = 0.1;
	my @pt = ();
	my @points = ();
	$function =~ s/\bx\b/\$x/g;
#	my @range = split(/[\s\,]+/, $range );
	my @range = split(' ', $range );

	print "function x range", @range , "\n" ;
	$x = $range[0];
	while ( $x <= $range[1] ){
		$y = (eval $function);
		print " while loop x: ", $x, " y: ", $y, "\n" ;
		$x += $delta;
		@pt = &Reverse_Transform($x,$y);
		push( @points, @pt);
		@pt = ();
	}
	$canvas->createLine( @points, -smooth => 'yes', -splinesteps => '2', -width => '1.0', -joinstyle => 'round', -tags => $curvetag);
	return( $curvetag );
}

sub Zoom_In(){
	my $scale = 2;
	$current_scale *= $scale;
	$canvas->scale( 'all', 140, 140, $scale, $scale );
}

sub Zoom_Out(){
	my $scale = 0.5;
	$current_scale *= $scale;
	$canvas->scale( 'all', 140, 140, $scale, $scale );
}

sub Full_View(){
	my $scale;
	if( $current_scale != 1 ){
		$scale = 1/$current_scale;
        	$canvas->scale( 'all', 140, 140, $scale, $scale );
		$current_scale = 1;
	} 
}

sub Select_Curve(){
	if( $canvas->itemcget('current', '-fill') eq 'red' ){
		$canvas->itemconfigure( 'selected', -fill => 'black' );
		$canvas->dtag('selected');
	} else {
		$canvas->addtag('selected', 'withtag', 'current' );
		$canvas->itemconfigure( 'selected', -fill => 'red' );
	}
}

=head1 NAME

graphPlotter-1.04.ptk

=head1 DESCRIPTION

graphPlotter is a simple perl/Tk application for plotting explicit polynomials.
i.e the function should be of the form y = F(x).

An example polynomial function would look like y = x^4 + x^2 - x + 7
or y = sin(x) or y = cos(x)

If the polynomial function is y = F(x), then the script takes following inputs:

1. Range of the variable x.

2. The function itself. i.e F(x).

3. The tag or name to be attached to the polynomial curve.

Following operations can be done using the menu items in the menu bar:

1. Zoom in
2. Zoom out
3. Refresh
4. Highlight curve


=head1 README

Following script is a graph plotter for the polynomials of the form y = F(x).
Trigonometric functions are also supported. This script can find its application 
in solving complex polynomial equations using graphs.

=head1 PREREQUISITES

This script requires the C<strict> module.  It also requires
C<Tk 800> and C<Math::Trig>.

=head1 COREQUISITES

Tk 800

=pod OSNAMES

Unix, Solaris, Linux

=head1 AUTHOR

Rohit Mishra E<lt>F<rohit@rohitmishra.com>E<gt>

=head1 COPYRIGHT

Copyright (c) 2006 Rohit Mishra <rohit@rohitmishra.com>. All rights reserved
This program is a free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=pod SCRIPT CATEGORIES

Educational/ComputerScience
Educational

=cut
