#!/usr/bin/perl use strict; #use warnings; use Gtk2 -init; use Gtk2::Ex::Dialogs; use Gtk2::Gdk::Keysyms; use Glib ':constants'; #use Goo::Canvas; use Data::Dumper; use Time::HiRes qw( gettimeofday tv_interval ); $|++; ########################### # setting up default values # edit these my $xbox = 400; my $ybox = 400; my $boxsize = 2; my $timer = 0; my $run = 0; my @birth_rules = (0,0,0,1,0,0,0,0,0); my @survival_rules = (0,0,1,1,0,0,0,0,0); ########################### my $sx; my $sy; my $t0 = [gettimeofday()]; my $fps = 0; my $it = 0; my $wh = pack("W", 255) x (3*$boxsize); my $bl = pack("W", 0) x (3*$boxsize); my $screen = pack("W", 255) x (3*$xbox*$ybox*$boxsize**2); my %pixels; my %neighbors; #print length $screen; toggle(100,100); toggle(101,100); toggle(102,100); toggle(102,101); toggle(101,102); #for my $y (0..$ybox-1) { for my $x (0..$xbox/5-1) {toggle(5*$x+1+$y%2, $y)}}; #for my $y (0..$ybox-1) {toggle(50, $y)} #my $start = 60; #for my $y ($start..$start+8) { for my $x (50..50+$y) {toggle($x+$y*34-$start*34-20, $y*40-$start*40+50)}}; #toggle(50,50); # Create the main window my $win = new Gtk2::Window ( "toplevel" ); $win->signal_connect ("delete_event", sub { $run = 0; print "Average fps = ", $fps/$it, "\n"; Gtk2->main_quit; }); #$win->signal_connect ("configure_event", \&win_expose); $win->set_title( "Fucked up Game of Life demo" ); $win->set_border_width (6); #$win->maximize; $win->set_resizable (0); $win->resize(700, 500); my $vbox = Gtk2::VBox->new (0, 6); $win->add ($vbox); # The DrawingArea that holds the pixbuf that holds the image my $da = Gtk2::DrawingArea->new; $da->set_size_request($xbox*$boxsize, $ybox*$boxsize); $vbox->pack_start($da, 1, 1, 0); $da->signal_connect('motion_notify_event' => \&on_background_motion_notify); $da->signal_connect('button_press_event' => \&on_background_motion_notify); $da->signal_connect('key_press_event' => \&on_key_press); $da->can_focus(TRUE); $da->set_events ([ @{ $da->get_events }, 'leave-notify-mask', 'pointer-motion-mask', 'pointer-motion-hint-mask', 'button-press-mask', 'key-press-mask', ]); #print $da->get_events ; $win->show_all; my $gc1 = Gtk2::Gdk::GC->new ($da->window); my $id = Glib::Timeout->add ($timer, \&timeout_handler); main Gtk2; ############################################## sub timeout_handler { update() if $run; return 1; # return 0 or 1 to kill/keep timer going } # Return: run/stop # space: single step # Keypad +/- : increase/decrease speed sub on_key_press { my ( $canvas, $event ) = @_; my $oldtimer = $timer; $run = !$run if $event->keyval == $Gtk2::Gdk::Keysyms{Return}; update(1) if $event->keyval == $Gtk2::Gdk::Keysyms{space}; $timer = int(1.1*$timer) > $timer+1 ? int(1.1*$timer) : $timer+1 if $event->keyval == $Gtk2::Gdk::Keysyms{KP_Subtract}; $timer = int(0.9*$timer) if $event->keyval == $Gtk2::Gdk::Keysyms{KP_Add} and $timer > 0; if ($oldtimer != $timer) { Glib::Source->remove($id); $id = Glib::Timeout->add ($timer, \&timeout_handler); } return TRUE; } # click to toggle pixels sub on_background_motion_notify { my ($da, $event) = @_; my (undef, $ex, $ey, $state) = $event->window->get_pointer; my $y = int(($ey)/$boxsize); #/// my $x = int(($ex)/$boxsize); return TRUE if ($x < 0 or $x >= $xbox or $y < 0 or $y >= $ybox); #print "$x\t$y\n"; #print Dumper $event; my $type = $event->type; if ($type eq 'motion-notify') { if ( $state >= 'button1-mask' ) { toggle($x, $y) unless $sx == $x and $sy == $y; update(); $sx = $x; $sy = $y; } } else { $sx = $x; $sy = $y; toggle($x, $y); update(); print " $x\t$y\n"; } # if ( $state >= 'button1-mask' ) { # toggle($x, $y) ;#unless $event->type eq 'motion-notify'; # update(); # } return TRUE; } sub update { my $doit = shift; #$screen = calculate_c($screen, $xbox, $ybox, $boxsize) if $run or $doit; if ($run or $doit) { calculate(); makescreen(); if ($it % 16) { my @y = keys %pixels; foreach my $y (@y) { delete $pixels{$y} unless scalar keys %{ $pixels{$y} }; } } } #print length $screen,"\n"; #print STDERR $screen; #print "\n"; my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_data ( $screen, 'rgb', FALSE, 8, $xbox*$boxsize, $ybox*$boxsize, 3*$xbox*$boxsize ); #my $gc1 = Gtk2::Gdk::GC->new ($da->window); $da->window->draw_pixbuf($gc1, $pixbuf, 0, 0, 0, 0, $xbox*$boxsize, $ybox*$boxsize, 'none', 0, 0, ); my $tt = 1/tv_interval($t0); $fps += $tt; $it++; print "$tt fps\r"; $t0 = [gettimeofday()]; return TRUE; } sub calculate { my ($left, $right, $top, $bot); %neighbors = (); foreach my $y (keys %pixels) { $top = ($y==0) ? $ybox-1 : -1; $bot = ($y==$ybox-1) ? 1-$ybox : 1; foreach my $x (keys %{ $pixels{$y} }) { $left = ($x==0) ? $xbox-1 : -1; $right = ($x==$xbox-1) ? 1-$xbox : 1; $neighbors{$y+$top}{$x+$left }++; $neighbors{$y }{$x+$left }++; $neighbors{$y+$bot}{$x+$left }++; $neighbors{$y+$top}{$x }++; $neighbors{$y+$bot}{$x }++; $neighbors{$y+$top}{$x+$right }++; $neighbors{$y }{$x+$right }++; $neighbors{$y+$bot}{$x+$right }++; $neighbors{$y }{$x } += 0; } } #my %xhash; my %yhash; #foreach my $x (keys %neighbors, keys %pixels) { $xhash{$x}++ } #foreach my $x (keys %xhash) { foreach my $y (keys %neighbors) { #%yhash = (); #foreach my $y (keys %{ $neighbors{$x} }, keys %{ $pixels{$x} }) { $yhash{$x}++ } #foreach my $y (keys %yhash) { foreach my $x (keys %{ $neighbors{$y} }) { #print "N$x\t$y\t$neighbors{$y}{$x}\n"; if ($pixels{$y}{$x}) { delete $pixels{$y}{$x} unless $survival_rules[ $neighbors{$y}{$x} ]; } else { $pixels{$y}{$x} = 1 if $birth_rules[ $neighbors{$y}{$x} ]; } #print "N$x\t$y\t$neighbors{$y}{$x}\t$pixels{$y}{$x}\n"; } } } sub makescreen { #my $row; my @xs = 0..$xbox-1; #foreach my $x (sort {$a <=> $b} keys %pixels) { # %{ $pixels{$x} } #} $screen = ''; foreach my $y (0..$ybox-1) { #foreach my $x (0..$xbox-1) { #print "P$x\t$y\t$pixels{$y}{$x}\n" if exists $pixels{$y}{$x}; #$row .= (exists $pixels{$y}{$x} ? $bl : $wh ); #} if (exists $pixels{$y}) { #$row = join '', map { exists $pixels{$y}{$_} ? $bl : $wh } (0..$xbox-1); $screen .= ( join '', map { exists $pixels{$y}{$_} ? $bl : $wh } @xs ) x $boxsize; } else { #$row = $wh x $xbox; $screen .= $wh x ($xbox*$boxsize); } #$screen .= $row x $boxsize; #$row = ''; } } sub toggle { my $x = shift; my $y = shift; my $pos = $y*$xbox*$boxsize**2*3+$x*$boxsize*3; my $v = substr $screen, $pos, $boxsize*3; for my $i (0..$boxsize-1) { substr $screen, $pos+$xbox*$i*$boxsize*3, $boxsize*3, ~$v; } if ($pixels{$y}{$x}) { delete $pixels{$y}{$x}; } else { $pixels{$y}{$x} = 1; } }