Commits

Herbert Breunung  committed afb45ec Draft

0.92 got linear math right and outsourced drawing into Wx::Perl::DrawMap

  • Participants
  • Parent commits a1f67e7

Comments (0)

Files changed (8)

File lib/Wx/Perl/DrawMap.pm

+
+use v5.12;
+use warnings;
+use Wx;
+
+package Wx::Perl::DrawMap;
+use base qw(Wx::StaticBitmap);
+use Scalar::Util qw(blessed looks_like_number);
+
+sub new {
+	my ($class, $parent, $x, $y) = @_;
+	$y = $x unless defined $y;
+
+	my ($self) = $class->SUPER::new($parent, -1, &Wx::wxNullBitmap);
+	$self->SetMinSize([$x, $y]);
+	$self->{'bitmap'} = Wx::Bitmap->new( $x, $y);
+	$self->{'dc'}  = Wx::MemoryDC->new();
+	$self->{'dc'}->SelectObject( $self->{'bitmap'} );
+	$self->{'colour'} = Wx::Colour->new(0,0,0,0);
+	$self->{'pen'} = Wx::Pen->new($self->{'colour'}, 2, &Wx::wxSOLID);
+	$self;
+}
+
+sub NewDrawing { $_[0]->{'dc'}->Clear(); $_[0] }
+sub PublishDrawing {
+	my ($self) = shift;
+	$self->SetBitmap( $self->{'bitmap'} );
+	$self->{'dc'}->SelectObject( $self->{'bitmap'} );
+	$self->Refresh();
+	$self;
+}
+
+sub Colour {
+	my ($self, $r, $g, $b) = @_;
+	$self->{'colour'}->Set( $r, $g, $b, 0 );
+	$self->{'pen'}->SetColour( $self->{'colour'} );
+	$self->{'dc'}->SetPen( $self->{'pen'} );
+	$self;
+}
+
+sub Point  {$_[0]->{'dc'}->DrawPoint( $_[1], $_[2] ); $_[0] }
+
+sub SaveAsFile {
+	my ($self, $file, $format) = @_;
+	return warn ((caller(0))[3].': need a file name as first parameter') unless defined $file;
+	if (not defined $format)           { $format = &Wx::wxBITMAP_TYPE_PNG }
+	elsif (looks_like_number($format) ){
+		return warn ((caller(0))[3].": unknown file format constant $format")
+			unless $format == &Wx::wxBITMAP_TYPE_PNG
+				or $format == &Wx::wxBITMAP_TYPE_JPEG
+				or $format == &Wx::wxBITMAP_TYPE_TIFF
+				or $format == &Wx::wxBITMAP_TYPE_BMP
+				or $format == &Wx::wxBITMAP_TYPE_XPM;
+	} else {
+		$format = lc $format;
+		if    ($format eq 'png') {$format = &Wx::wxBITMAP_TYPE_PNG}
+		elsif ($format eq 'jpg') {$format = &Wx::wxBITMAP_TYPE_JPEG}
+		elsif ($format eq 'jpeg'){$format = &Wx::wxBITMAP_TYPE_JPEG}
+		elsif ($format eq 'tif') {$format = &Wx::wxBITMAP_TYPE_TIFF}
+		elsif ($format eq 'tiff'){$format = &Wx::wxBITMAP_TYPE_TIFF}
+		elsif ($format eq 'bmp') {$format = &Wx::wxBITMAP_TYPE_BMP}
+		elsif ($format eq 'xpm') {$format = &Wx::wxBITMAP_TYPE_XPM}
+		else {return warn ((caller(0))[3].": unknown file format: $format")}
+	}
+	# in later versions use: ->ConvertToImage() 
+	Wx::Image->new( $self->GetBitmap() )->SaveFile($file, $format);  
+	$self;
+}
+1;

File lib/Wx/Perl/RadioGroup.pm

 use warnings;
 
 package Wx::Perl::RadioGroup;
+
 use base qw(Wx::Panel);
+use Scalar::Util qw(blessed looks_like_number);
+
+
 sub new {
-	my ($class, $parent, $default, $label, $orient, $callback) = @_;
-	return unless ref $label eq 'ARRAY';
+	my ($class, $parent, $label, $default, $orient, $callback) = @_;
+	return warn ((caller(0))[3].' needs a Widget ref as first parameter')
+		unless blessed($parent) and $parent->isa('Wx::Window');
+	return warn ((caller(0))[3].' needs a ARRAY ref as second parameter')
+		unless ref $label eq 'ARRAY';
+
+	$default = 0 if not defined $default or not $default;
+	for (0 .. $#{$label}){ $default = $_ if $default eq $label->[$_] }
 	$orient = &Wx::wxHORIZONTAL unless defined $orient;
 	$callback = sub {} if not defined $callback or ref $callback ne 'CODE';
 	

File lib/Wx/Perl/Smart/Factory.pm

+use v5.12;
+use warnings;
+use Wx;
+
+package Wx::Perl::Smart::Factory;
+our $VERSION = '0.1';
+use Scalar::Util qw(blessed looks_like_number);
+
+sub Make {
+	my @widget;
+	for my $widget_def (@_){
+		next unless defined $widget_def;
+		warn ((caller(0))[3].': need an ARRAY ref as parameter, not $widget_def'), next unless ref $widget_def eq 'ARRAY';
+		my $widget;
+		if ($widget_def->[0] eq '-Button'){
+			warn ((caller(0))[3].': @$widget_def is no proper WxButton definition (-Button => $parent, "label", &callback)'),
+				next if not(blessed($widget_def->[1]) and $widget_def->[1]->isa('Wx::Window'))
+				or ref $widget_def->[2]
+				or ref $widget_def->[3] ne 'CODE';
+			$widget = Wx::Button->new($widget_def->[1], -1, $widget_def->[2]);
+			Wx::Event::EVT_BUTTON( $widget, $widget, $widget_def->[3] );
+		} elsif ($widget_def->[0] eq '-CheckBox'){
+			warn ((caller(0))[3].': @$widget_def is no proper Checkbox definition (-CheckBox => $parent, "label", checked?, &callback)'),
+				next if not(blessed($widget_def->[1]) and $widget_def->[1]->isa('Wx::Window'))
+				or ref $widget_def->[2]
+				or not looks_like_number($widget_def->[3])
+				or ($widget_def->[4] and ref $widget_def->[4] ne 'CODE');
+			$widget = Wx::CheckBox->new($widget_def->[1], -1, $widget_def->[2]);
+			$widget->SetValue($widget_def->[3]);
+			Wx::Event::EVT_CHECKBOX( $widget, -1, $widget_def->[4] ) if $widget_def->[4];
+		} elsif ($widget_def->[0] eq '-ComboBox'){
+			warn ((caller(0))[3].': @$widget_def is no proper ComboBox definition (-ComboBox => $parent, [options], "visible value", length, &callback)'),
+				next if not(blessed($widget_def->[1]) and $widget_def->[1]->isa('Wx::Window'))
+				or ref $widget_def->[2] ne 'ARRAY'
+				or ref $widget_def->[3] 
+				or ref $widget_def->[4]
+				or ($widget_def->[5] and ref $widget_def->[5] ne 'CODE');
+			my $textvalue = $widget_def->[3];
+			if (not defined $textvalue or not $textvalue){$textvalue = $widget_def->[2][0]   } 
+			elsif (looks_like_number($textvalue)) {$textvalue = $widget_def->[2][$textvalue] }
+			my $length = $widget_def->[4];
+			$length = -1 unless $length;
+			$widget = Wx::ComboBox->new(
+				$widget_def->[1], -1, $textvalue, [-1,-1], [$length,-1], $widget_def->[2]
+			);
+			Wx::Event::EVT_COMBOBOX( $widget, -1, $widget_def->[5]) if $widget_def->[5];
+		}
+		push @widget, $widget if defined $widget and blessed($widget);
+	}
+	return $widget[0] if scalar @widget == 1;
+	return @widget;
+}
+
+
+1;

File lib/Wx/Perl/Smart/Frame.pm

 use Wx;
 use Wx::Perl::Smart::Sizer;
 use Wx::Perl::Smart::Panel;
+use Wx::Perl::Smart::Factory;
 
 package Wx::Perl::Smart::Frame;
 our $VERSION = '0.20';
 
 my %widget_io = (
 	'Wx::ComboBox' =>1, 'Wx::CheckBox' =>1, 
+	'Wx::TextCtrl' =>1, 'Wx::StyledTextCtrl' =>1, 
 	'Wx::Perl::TextSlider' => 1, 'Wx::Perl::RadioGroup' =>1,
 );
 
 	$self;
 }
 
-
-sub CreateSubWidgets {
-	my ($self) = shift;
-	my @widget;
-	for my $widget_def (@_){
-		next unless ref $widget_def eq 'ARRAY';
-		my $widget = $self->CreateSubWidget($widget_def);
-		push @widget, $widget if $widget;
-	}
-	return @widget;
-}
-
-sub CreateSubWidget {
-	my ($self, $widget_def) = @_;
-	return warn ((caller(0))[3].': need an ARRAY ref as parameter') unless ref $widget_def eq 'ARRAY';
-	my $widget;
-	if ($widget_def->[0] eq '-Button'){
-		warn ((caller(0))[3].': @$widget_def is no proper WxButton definition ("label", &callback)'),
-			next if ref $widget_def->[1] or ref $widget_def->[2] ne 'CODE';
-		$widget = Wx::Button->new($self, -1, $widget_def->[1]);
-		Wx::Event::EVT_BUTTON( $widget, $widget, $widget_def->[2] );
-	} elsif ($widget_def->[0] eq '-CheckBox'){
-		warn ((caller(0))[3].': @$widget_def is no proper Checkbox definition ("label", checked?, &callback)'),
-			next if ref $widget_def->[1]
-			or not looks_like_number($widget_def->[2])
-			or ref $widget_def->[3] ne 'CODE';
-		$widget = Wx::CheckBox->new($self, -1, $widget_def->[1]);
-		$widget->SetValue($widget_def->[2]);
-		Wx::Event::EVT_CHECKBOX( $widget, -1, $widget_def->[3] );
-	}
-	return $widget if blessed($widget);
-}
-
-
 sub SubscribeWidgets {
 	my ($self, $widgets) = @_;
 	warn ((caller(0))[3].'needs a hashref as parameter') unless ref $widgets eq 'HASH';
 	while (my ($name, $widget) = each %$widgets) {
 		warn ((caller(0))[3].": $name is already subscribed"), next if $self->{'subwidget'}{$name};
-		$widget = $self->CreateSubWidget($widget) if ref $widget eq 'ARRAY';
+		if (ref $widget eq 'ARRAY'){
+			splice (@$widget, 1, 0, $self);          # frames becomes the parent
+			$widget = Wx::Perl::Smart::Factory::Make($widget);
+		}
 		warn ((caller(0))[3].": $widget is not a widget"), next
 			unless blessed($widget) and $widget->isa('Wx::Window');
 		$self->{'subwidget'}{$name} = $widget;
 		$self->{'default_val'}{$name} = $widget->GetValue if $widget_io{ref $widget};
+		$widget->Reparent($self);
 		$widget->Show(0);
 	 }
 }
 	}
 	$self;
 }
-sub ResetValues { shift->ResetValue }     # for naming consistency
+sub ResetValues { $_[0]->ResetValue }     # for naming consistency
 sub ResetValue {
 	my ($self) = shift;
-	my $which = shift // 'all';
-	return warn ((caller(0))[3].": $which is not a subscribed widget")
-		unless exists $self->{'default_val'}{$which} or $which eq 'all';
-	if ($which eq 'all'){
+	my ($name) = shift // ':all';   # value name
+	return warn ((caller(0))[3].": $name is not a subscribed widget")
+		unless exists $self->{'default_val'}{$name} or $name eq ':all';
+	if ($name eq ':all'){
 		$self->{'subwidget'}{$_}->SetValue( $self->{'default_val'}{$_} )
 			for keys %{ $self->{'default_val'} };
 	} 
-	else { $self->{'subwidget'}{$which}->SetValue( $self->{'default_val'}{$which} ) }
+	else { $self->{'subwidget'}{$name}->SetValue( $self->{'default_val'}{$name} ) }
 	$self;
 }
 

File lib/Wx/Perl/Smart/Panel.pm

 use warnings;
 use Wx;
 use Wx::Perl::Smart::Sizer;
+use Wx::Perl::Smart::Factory;
 
 package Wx::Perl::Smart::Panel;
 use base qw(Wx::Panel);

File lib/Wx/Perl/Smart/Sizer.pm

 use v5.12;
 use warnings;
 use Wx;
-use Wx::Perl::Smart::LabeledBox;
-use Wx::Perl::Smart::TabbedBox;
-use Wx::Perl::RadioGroup;
-use Wx::Perl::TextSlider;
+#use Wx::Perl::Smart::LabeledBox;
+#use Wx::Perl::Smart::TabbedBox;
+use Wx::Perl::Smart::Factory;
+
+#use Wx::Perl::RadioGroup;
+#use Wx::Perl::TextSlider;
 
 package Wx::Perl::Smart::Sizer;
 our $VERSION = '0.23';
 					next unless ref $widget eq 'ARRAY' and scalar @$widget > 0 and not ref $widget->[0];
 					$pos++;
 					my $label = shift @$widget;
+					require Wx::Perl::Smart::LabeledBox;
 					$widget = Wx::Perl::Smart::LabeledBox->new($parent, $label, $widget);
 				}
 				elsif ($widget eq 'TabbedBox' ) {
 					$widget = $widgets->[$pos+1];
 					next unless ref $widget eq 'ARRAY' and scalar @$widget > 0 and not ref $widget->[0];
 					$pos++;
+					require Wx::Perl::Smart::TabbedBox;
 					$widget = Wx::Perl::Smart::TabbedBox->new(
 						$parent, $widget, toggle_orientation($orientation),
 						{add_callback => $arg{'add_callback'}}

File lib/harmonograph.pl

-#!/usr/bin/perl
+#!/usr/bin/perl
+
 use v5.12;
 use warnings;
 use Wx;
-use Wx::Perl::Smart::Frame;
+use Wx::Perl::DrawMap;
+use Wx::Perl::TextSlider;
+use Wx::Perl::RadioGroup;
+use Wx::Perl::Smart::Frame;
 use Colour::Flow;
 use Math::Trig;
 use File::Spec;
 use YAML::Tiny;
 
 package Harmonograph;
-our $VERSION = '0.90';
+our $VERSION = '0.92';
 use base qw(Wx::App);
 
-my $boardsize = 560;
-my (%l18n, %range_defaults, $midoffset, $max_amp, );
 my $fav_file = 'favs.yml'; 
 my $l18n_file = 'localisation.yaml';
 my $language = 'german';
 sub OnInit {
 	my $app = shift;
 
-	# load localisation - texts in chosen language
+	# load localisation texts in chosen language
 	my ($v, $dir, $f) = File::Spec->splitpath(__FILE__);
 	$l18n_file = File::Spec->catfile($dir, $l18n_file) if $dir;
-	die "$l18n_file is missing " unless -e $l18n_file;
-	%l18n = %{YAML::Tiny->read( $l18n_file )->[0]{$language}};
-
+	die "localisation file $l18n_file is missing!" unless -e $l18n_file;
+	my %l18n = %{YAML::Tiny->read( $l18n_file )->[0]{$language}};
+	$app->{'l18n'} = \%l18n;
+
+	# loading the numbers of the remembered favorites
+	$app->{'favorites'} = -e $fav_file ? YAML::Tiny->read( $fav_file ) : YAML::Tiny->new;
+	my @remembered_pic_names = ref $app->{'favorites'}->[0] eq 'HASH' ? keys $app->{'favorites'}->[0] : ();
+
+	# making UI
 	my $frame = $app->{'frame'} = Wx::Perl::Smart::Frame->new(__PACKAGE__." $VERSION");
-
-	$midoffset = $boardsize / 2;
-	$max_amp = $midoffset - 10;   # max amplitude
-	%range_defaults = ( # label, min, max, init
-		frequency_x => ['X',               1, 1,  30], frequency_y => ['Y',              1,  1,   30],
-		amplitude_x => ['X', $max_amp, 0, $max_amp*2], amplitude_y => ['Y',       $max_amp,  0,$max_amp*2],
-		rotation    => [$l18n{'amount'},   1, 0,  30], friction    => [$l18n{'friction'},0,  0,   50],
-		length      => [$l18n{'length'},  12, 1, 100], density     => [$l18n{'density'},90,  1, 1000],
-		thickness   => [$l18n{'thickness'},1, 1,  12], zoom        => [$l18n{'zoom'},    0,-10,   10], 
-		start_colour=> [$l18n{'start'},    0, 0,1500], flow_colour => [$l18n{'flow'},    0,  0,   30],
+
+	my $boardsize = Wx::GetDisplaySize()->GetHeight() - 250;
+	my $origin_offset = $app->{'origin_offset'} = $boardsize / 2;
+	my $max_amp = $app->{'max_amp'} = $origin_offset - 10;        # max amplitude
+	my $repaint = sub { $app->Repaint() };
+	my %range_defaults = (# label, min, max, init
+		frequency_x => ['X',               1, 1,  30], frequency_y => ['Y',               1,  1,   30],
+		amplitude_x => ['X',               0, 0, 360], amplitude_y => ['Y',               0,  0,  360],
+		rotation    => [$l18n{'amount'},   1, 0,  30], friction    => [$l18n{'friction'}, 0,  0,  200],
+		length      => [$l18n{'length'},  12, 1, 100], density     => [$l18n{'density'},100,  1,  100],
+		thickness   => [$l18n{'thickness'},1, 1,  12], zoom        => [$l18n{'zoom'},     0,-10,   10], 
+		start_colour=> [$l18n{'start'},    0, 0,1500], flow_colour => [$l18n{'flow'},     0,  0,   30],
 		scale_colour=> [$l18n{'scale'},    1, 1,   4],
 	);
-	my $repaint = sub { $app->Repaint() };
+	$frame->SubscribeWidgets
+		({$_         => Wx::Perl::TextSlider->new ($frame, @{$range_defaults{$_}}, $repaint)}) for keys %range_defaults;
 	$frame->SubscribeWidgets({
-		$_ => Wx::Perl::TextSlider->new ($frame, @{$range_defaults{$_}}, $repaint)
-	}) for keys %range_defaults;
+		drawboard    => Wx::Perl::DrawMap->new($frame, $boardsize),
+		fav_select   => [-ComboBox => \@remembered_pic_names, 0, -1, sub {
+										$frame->SetValues( $app->{'favorites'}[0]{ $_[0]->GetValue() } );$app->Repaint()}],
+		format_select=> [-ComboBox => [qw(PNG JPG TIFF BMP XPM)], 0, 70       ],
+		save         => [-Button   => $l18n{'save'},    sub {$app->Save()    }],
+		save_all     => [-Button   => $l18n{'all'},     sub {$app->SaveAll() }],
+		remember     => [-Button   => $l18n{'remember'},sub {$app->Remember()}],
+		forget       => [-Button   => $l18n{'forget'},  sub {$app->Forget()  }],
+		y_invers     => [-CheckBox => $l18n{'y_inverse'}, 0, $repaint],
+		rotation_dir => Wx::Perl::RadioGroup->new($frame, [@l18n{'no','left','right'}], 0, &Wx::wxHORIZONTAL, $repaint),
+	});
 
-	$app->{'favorites'} = -e $fav_file ? YAML::Tiny->read( $fav_file ) : YAML::Tiny->new;
-	my ($fsaved, @saved, ) = ( '', ());
-	if (ref $app->{'favorites'}->[0] eq 'HASH'){
-		@saved = keys $app->{'favorites'}->[0];
-		$fsaved = $saved[0];
-	}
-	$app->{'fav_select'} = Wx::ComboBox->new($frame, -1, $fsaved,[-1,-1],[-1,-1], \@saved);
-	Wx::Event::EVT_COMBOBOX($app->{'fav_select'}, -1, sub {
-		$app->{'frame'}->SetValues( $app->{'favorites'}[0]{ $app->{'fav_select'}->GetValue() } );
-		$app->Repaint();
-	});
-	$frame->SubscribeWidgets({
-		drawboard   => Wx::StaticBitmap->new($frame, -1, &Wx::wxNullBitmap),
-		fav_select  => $app->{'fav_select'},
-		save        => [-Button   => $l18n{'save'},    sub {$app->Save()    }],
-		save_all    => [-Button   => $l18n{'all'},     sub {$app->SaveAll() }],
-		remember    => [-Button   => $l18n{'remember'},sub {$app->Remember()}],
-		forget      => [-Button   => $l18n{'forget'},  sub {$app->Forget()  }],
-		y_invers    => [-CheckBox => $l18n{'y_inverse'}, 0, $repaint],
-		rotation_dir => Wx::Perl::RadioGroup->new($frame, 0, [$l18n{'no'}, $l18n{'left'}, $l18n{'right'}], &Wx::wxHORIZONTAL, $repaint)
-	});
-	$frame->w('drawboard')->SetMinSize([$boardsize, $boardsize]);
 	$frame->SetSmartLayout([
 		# left part
 		'<drawboard>',
 		10,
 		{border => 10, flags => &Wx::wxALL|&Wx::wxGROW},
 		'<fav_select>',
-		['<save>', 20, '<save_all>', \1, '<forget>', 20, '<remember>'],
+		['<format_select>', 10,'<save>', 10, '<save_all>', \1, '<forget>', 10, '<remember>'],
 	],[ # right half
 		-TabbedBox =>  [ 
 			$l18n{'oscillators'} => [[
 			]],
 			$l18n{'visuals'} => [[
 				{border => 5, flags => &Wx::wxGROW|&Wx::wxALL},
-				-LabeledBox => [ $l18n{'line'}  => [qw( <length> <density> <thickness>             )]],
+				-LabeledBox => [ $l18n{'line'}  => [qw( <length> <density>                         )]], # <thickness>
 				-LabeledBox => [ $l18n{'color'} => [qw( <start_colour> <flow_colour> <scale_colour>)]],
 				{border => 10},							'<zoom>',
 			]]
 		],
-	],);
-
-	$app->{'bmp'}   = Wx::Bitmap->new( $boardsize, $boardsize);
-	$app->{'dc'}  = Wx::MemoryDC->new();
-	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-
+	],);#~line
 	$frame->ResetValues();
 	$app->Repaint();
 	$app->SetTopWindow($frame);
 	1;
 }
-sub OnExit {
-	my $app = shift;
-	$app->{'favorites'}->write( $fav_file );
-	1;
-}
-
+
 
 sub Remember {
-	my $app = shift;
-	my $name = Wx::GetTextFromUser(
-		$l18n{'ask_for_a_name'}, __PACKAGE__." $VERSION", '', $app->{'frame'}
-	);
-	$name = Wx::GetTextFromUser(
-		$l18n{'ask_another_name'}, __PACKAGE__." $VERSION", '', $app->{'frame'}
-	) while exists $app->{'favorites'}[0]{$name} or not $name;
+	my $app = shift;
+	my $frame = $app->{'frame'};
+	my $cb = $frame->w('fav_select'); # combo box
+	my $name = Wx::GetTextFromUser
+		($app->{'l18n'}{'ask_for_a_name'}, __PACKAGE__." $VERSION", '', $frame);
+	$name = Wx::GetTextFromUser
+		($app->{'l18n'}{'ask_another_name'}, __PACKAGE__." $VERSION", '', $frame)
+			while exists $app->{'favorites'}[0]{$name} or not $name;
 	$app->{'favorites'}[0]{$name} = $app->{'frame'}->GetValues;
-	$app->{'fav_select'}->SetValue($name);
-	$app->{'fav_select'}->Insert($name, 0);
+	$cb->SetValue($name);
+	$cb->Insert($name, 0);
 	$app->{'favorites'}->write( $fav_file );
 }
 sub Forget {
 	my $app = shift;
-	my $cb = $app->{'fav_select'};
+	my $cb = $app->{'frame'}->w('fav_select');
 	return if $cb->IsEmpty;
 	my $name = $cb->GetValue();
 	$cb->Delete( $cb->FindString($name) );
 
 
 sub Save {
-	my $app = shift;
+	my $app = shift;
+	my $format = lc $app->{'frame'}->w('format_select')->GetValue();
 	my $file = Wx::FileSelector(
-		$l18n{'save_under'}, '.', $app->{'fav_select'}->GetValue().'.png',
-		'', '(*)|*', &Wx::wxFD_SAVE, $app->{'frame'}
+		$app->{'l18n'}{'save_under'}.' '.uc($format),            '.', 
+		$app->{'frame'}->w('fav_select')->GetValue().".$format",  '',
+		'(*)|*', &Wx::wxFD_SAVE, $app->{'frame'}
 	);
-	return unless $file;
-	my $img = Wx::Image->new( $app->{'frame'}->w('drawboard')->GetBitmap() );  # in later versions use: ->ConvertToImage() 
-	$img->SaveFile($file, &Wx::wxBITMAP_TYPE_JPEG ); #wxBITMAP_TYPE_BMP
+	$app->{'frame'}->w('drawboard')->SaveAsFile($file, $format) if $file;
 }
 sub SaveAll {
 	my $app = shift;
-	my $frame = $app->{'frame'};
-	my $dir = Wx::DirSelector( $l18n{'save_all_under'}, '.', 0, [-1,-1], $app->{'frame'});
+	my $frame = $app->{'frame'};
+	my $format = lc $app->{'frame'}->w('format_select')->GetValue();
+	my $dir = Wx::DirSelector( $app->{'l18n'}{'save_all_under'}.' '.uc($format), '.', 0, [-1,-1], $frame);
 	return unless -d $dir;
 	my $values = $frame->GetValues();
 	for (keys $app->{'favorites'}[0]) {
 		$frame->SetValues( $app->{'favorites'}[0]{$_} );
 		$app->Repaint();
-		my $file = File::Spec->catfile($dir, $_.'.png' );
-		my $img = Wx::Image->new( $frame->w('drawboard')->GetBitmap() );  # in later versions use: ->ConvertToImage() 
-		$img->SaveFile($file, &Wx::wxBITMAP_TYPE_PNG ); # _BMP _JPEG
+		my $file = File::Spec->catfile($dir, "$_.$format" );
+		$frame->w('drawboard')->SaveAsFile($file, $format);
 	}
 	$frame->SetValues($values);
 	$app->Repaint();
 
 sub Repaint {
 	my $app = shift;
-	my $value = $app->{'frame'}->GetValues;
+	my $board = $app->{'frame'}->w('drawboard');
+	my $v = $app->{'frame'}->GetValues; # values
+
+	my $origin_offset   = $app->{'origin_offset'};
+	my $max_amp         = $app->{'max_amp'} * (1.2**$v->{'zoom'});
+	my $win_size_factor = $max_amp / 10;
+	my $pi = 3.1415926536; 
+	my $xangle = $v->{'amplitude_x'} / 360 * $pi;                               # beginning with start amplitudes
+	my $yangle = $v->{'amplitude_y'} / 360 * $pi;
+	my $dx = $v->{'frequency_x'};                                               # one step of the oscillator rotation 
+	my $dy = $v->{'frequency_y'};
+	my $max_freq = $dx > $dy ? $dx : $dy;
+	my $step_size = 100 / $v->{'density'} / $max_freq / $win_size_factor;
+	$dx *= 0.2 * $step_size;                                                   # one step of the oscillator rotation 
+	$dy *= 0.2 * $step_size;
+	my $friction_factor =  (1 - (0.00005 * $v->{'friction'})) ** $step_size;
+	my $steps = $v->{'length'} * 100 * $v->{'density'};
 
-	#my $pi = 3.141592654; # deg2rad($degrees);
-	my $line_density =   10000 / (1.02**$value->{'density'});
-	my $dx = $line_density / $value->{'frequency_x'};
-	my $dy = $line_density / $value->{'frequency_y'};
-	$dy = $value->{'y_invers'} ? - $dy : $dy;
-	my $x = Math::Trig::asin( ($value->{'amplitude_x'} - $max_amp)/$max_amp );
-	my $y = Math::Trig::asin( ($value->{'amplitude_y'} - $max_amp)/$max_amp );
-	my $drot = $value->{'rotation'} / 2000;
-	my $rota_dir = $value->{'rotation_dir'};
-	$drot = - $drot if $rota_dir == 2;
-	my $friction = 1 - 0.000001 * $value->{'friction'};
-	$value->{'zoom'} = 1.2**$value->{'zoom'};
-	my $rot = 0;
-	my $cur_amp = $max_amp;
+	#$dy = $v->{'y_invers'} ? - $dy : $dy;
+	#my $x = Math::Trig::asin( ($v->{'amplitude_x'} - $max_amp)/$max_amp );
+	#my $y = Math::Trig::asin( ($v->{'amplitude_y'} - $max_amp)/$max_amp );
+	#my $drot = $v->{'rotation'} / 2000;
+	#my $rota_dir = $v->{'rotation_dir'};
+	#$drot = - $drot if $rota_dir == 2;
+	#my $rot = 0;# deg2rad($degrees);
 	my $colour_flow = Colour::Flow->new(
-		$value->{'scale_colour'} ,
-		($value->{'flow_colour'} ? 1000 / $value->{'flow_colour'} : 0), # change rate
-		$value->{'start_colour'} ,
-	);
-	$app->{'dc'}->Clear();
-	my $colour = Wx::Colour->new($colour_flow->get_rgb(), 0);
-	my $pen = Wx::Pen->new($colour, $value->{'thickness'}, &Wx::wxSOLID);
-	$app->{'dc'}->SetPen( $pen );
+		 $v->{'scale_colour'},
+		($v->{'flow_colour'} ? 10 / $v->{'flow_colour'} * $v->{'density'} : 0), # change rate
+		 $v->{'start_colour'},
+	);
+	$board->Colour( $colour_flow->get_rgb() );
+	$board->NewDrawing();
+
+	for my $cc (0 .. $steps) {
+		$xangle += $dx;
+		$yangle += $dy;
+		$max_amp *= $friction_factor;
+
+		#my $xs = sin($x += $dx);
+		#my $ys = sin($y += $dy);
+		#$rot += $drot;
+		#($xs, $ys) = rotate($xs, $ys, $rot) if $rota_dir;
+		$board->Colour( $colour_flow->get_rgb() ) if $colour_flow->next_step(); 
 
-	for my $cc (0 .. $value->{'length'} *4000) {
-		my $xs = sin($x += $dx);
-		my $ys = sin($y += $dy);
-		$cur_amp *= $friction;
-		$rot += $drot;
-		($xs, $ys) = rotate($xs, $ys, $rot) if $rota_dir;
-		if ($colour_flow->next_step()){
-			$colour->Set( $colour_flow->get_rgb(), 0 );
-			$pen->SetColour( $colour );
-			$app->{'dc'}->SetPen( $pen );
-		}
-		$app->{'dc'}->DrawPoint( ($xs*$cur_amp*$value->{'zoom'})+$midoffset,
-		                         ($ys*$cur_amp*$value->{'zoom'})+$midoffset );
-	}
-	$app->{'frame'}->w('drawboard')->SetBitmap( $app->{'bmp'} );
-	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-	$app->{'frame'}->w('drawboard')->Refresh();
+		#$app->{'dc'}->DrawPoint( ($xs*$cur_amp*$value->{'zoom'})+$midoffset,
+		#                         ($ys*$cur_amp*$value->{'zoom'})+$midoffset );
+
+		$board->Point(sin($xangle) * $max_amp + $origin_offset, 
+					  sin($yangle) * $max_amp + $origin_offset);
+	}
+	$board->PublishDrawing();
 }
 sub rotate {
 	my ($x, $y, $r) = @_;

File lib/localisation.yaml

   right: Right
   rotation: Rotation
   save: 'Save'
-  save_all_under: 'Save All Remebered as PNG Files in Directory ...'
-  save_under: 'Save Picture as PNG File under ...'
+  save_all_under: 'Choose Directory where to Save All Remebered as'
+  save_under: 'Save Picture in the File Format'
   scale: Scale
   start: Start
   start_amp: 'Starting Amplitude'
   right: Rechts
   rotation: Rotation
   save: 'Speichern'
-  save_all_under: 'Speicher alle Gemerkten als PNG im Verzeichnis ...'
-  save_under: 'Speichere Bild als PNG Datei unter ...'
+  save_all_under: 'Waehle Verzeichnis wo alle Gemerkten gespeichert werden als'
+  save_under: 'Speichere Bild in Dateiformat'
   scale: Skala
   start: Start
   start_amp: Startamplitude