1. Herbert Breunung
  2. App::Harmonograph

Commits

Herbert Breunung  committed d870070 Draft

update to 0.89

  • Participants
  • Parent commits a45e13b
  • Branches default

Comments (0)

Files changed (8)

File README

View file
  • Ignore whitespace
 and moving thereby a pencil. Sometimes the paper issimultaneously rotated
 by a third pendulum. This program enhances the original hormonograph
 by additional options like rotation speeds, start aplitudes, friction,
-dot intervals and different color flows.
+duration, dot intervals and different color flows.
 
 The comman line version is ment to just produce JPG/PNG/BMP/XPM
 with a given set of parameters. (planned)

File bin/harmonograph.pl

  • Ignore whitespace
-#!/usr/bin/perl
-use v5.12;
-use warnings;
-use Wx;
-use Wx::Perl::RadioGroup;
-use Wx::Perl::TextSlider;
-use YAML::Tiny;
-use Math::Trig;
-
-
-package Harmonograph;
-our $VERSION = '0.78';
-use base qw(Wx::App);
-my $maltafelgroesse = 500;
-my $midoffset = $maltafelgroesse / 2;
-my $amp = 220;
-my %range_defaults = ( # label, min, max, init
-    freqx => ['X',      1, 1,    27], freqy    => ['Y',      1, 1,     27],
-    ampx  => ['X',   $amp, 0,$amp*2], ampy     => ['Y',   $amp, 0, $amp*2],
-    rota  => ['Betrag', 1, 0,    20], friction => ['Reibung',0, 0,    100],
- 'length' => ['Dauer', 12, 0,   100],
-     zoom => ['Zoom',   0,-10,   10], color    => ['Farbe',  0, 0,     30],
-);
-
-
-sub OnInit {
-	my $app = shift;
-	my $frame = Wx::Frame->new(
-		undef, -1, __PACKAGE__." $VERSION", [-1,-1],[700,-1],
-        &Wx::wxCAPTION | &Wx::wxCLOSE_BOX | &Wx::wxFRAME_TOOL_WINDOW | &Wx::wxSYSTEM_MENU | &Wx::wxRESIZE_BORDER
-	);
-	$frame->SetIcon( Wx::GetWxPerlIcon() );
-
-	$app->{'maltafel'} = Wx::StaticBitmap->new($frame, -1, &Wx::wxNullBitmap);
-	$app->{'maltafel'}->SetMinSize([$maltafelgroesse, $maltafelgroesse]);
-	$app->{'bmp'}   = Wx::Bitmap->new( $maltafelgroesse, $maltafelgroesse);
-	$app->{'dc'}  = Wx::MemoryDC->new();
-	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-
-	my $panel = $app->{'panel'} = Wx::Panel->new($frame);                       # right side control panel
-	my %sizer;
-	$sizer{'main'} = Wx::BoxSizer->new(&Wx::wxVERTICAL);
-	my $callback = sub { $app->repaint() };
-	my $sizer_al = &Wx::wxALL|&Wx::wxGROW;
-	$panel->{$_} = Wx::Perl::TextSlider->new
-		($panel, @{$range_defaults{$_}}, $callback) for keys %range_defaults;
-
-	$panel->{'yinvers'} = Wx::CheckBox->new($panel, -1,'Y - Richtung invers');
-	Wx::Event::EVT_CHECKBOX( $panel->{'yinvers'}, -1, $callback );
-
-	$panel->{'rot_richtung'} = Wx::Perl::RadioGroup->new(
-		$panel, 'keine', [qw(keine links rechts)], &Wx::wxHORIZONTAL, $callback
-	);
-
-	 Wx::Perl::Box->new();
-	for my $label (qw(Frequenz Startamplitude Rotation)){
-		my $bname = lc substr($label, 0, 4) . '_box';
-		$sizer{$bname} = Wx::StaticBoxSizer->new
-			( Wx::StaticBox->new($panel, -1, " $label "), &Wx::wxVERTICAL);
-		$sizer{'main'}->Add($sizer{$bname}, 0, $sizer_al, 5);
-	}
-	$sizer{'freq_box'}->Add($panel->{ $_ },       0, $sizer_al, 5) for qw(freqx freqy);
-	$sizer{'freq_box'}->Add($panel->{'yinvers'}, 0, &Wx::wxLEFT, 45 );
-	$sizer{'freq_box'}->AddSpacer(5);
-	$sizer{'star_box'}->Add($panel->{ $_ },       0, $sizer_al, 5) for qw(ampx ampy);
-	$sizer{'rota_box'}->Add($panel->{'rot_richtung'},  0, &Wx::wxLEFT, 10);
-	$sizer{'rota_box'}->Add($panel->{'rota'}, 0, $sizer_al, 5);
-	$sizer{'main'}->Add( $panel->{'friction'},     0, $sizer_al, 5);
-	$sizer{'main'}->Add( $panel->{'length'  },     0, $sizer_al, 5);
-	$sizer{'main'}->Add( $panel->{'zoom'  },       0, $sizer_al, 5);
-	$sizer{'main'}->Add( $panel->{'color' },       0, $sizer_al, 5);
-	$sizer{'main'}->AddSpacer(10);
-	$panel->SetSizerAndFit( $sizer{'main'} );
-
-	my $sizer = Wx::BoxSizer->new( &Wx::wxHORIZONTAL);
-	$sizer->Add( $app->{'maltafel'}, 1, &Wx::wxALL|&Wx::wxGROW, 0);
-	$sizer->Add( $app->{'panel'},    1, &Wx::wxALL|&Wx::wxGROW, 0);
-	$frame->SetSizerAndFit( $sizer );
-
-	$app->set_defaults('all');
-	$app->repaint();
-	$frame->Center();
-	$frame->Show(1);
-	$app->SetTopWindow($frame);
-	1;
-}
-
-sub set_defaults {
-	my $app = shift;
-	my $which = shift;
-	if ($which eq 'all'){
-		$app->{'panel'}{'rot_richtung'}->ResetValue();
-		$app->set_defaults($_) for keys %range_defaults;
-	} 
-	else { $app->{'panel'}{$which}->ResetValue }
-	$app;
-}
-
-sub repaint {
-	my $app = shift;
-	
-	$app->{'dc'}->Clear();
-	my $panel = $app->{'panel'};
-	my $pi = 3.141592654; # deg2rad($degrees);
-	my $dx = .01 / $panel->{'freqx'}->GetValue();
-	my $dy = .01 / $panel->{'freqy'}->GetValue();
-	$dy = $panel->{'yinvers'}->IsChecked() ? - $dy : $dy;
-	my $x = Math::Trig::asin( ($panel->{'ampx'}->GetValue - $amp)/$amp );
-	my $y = Math::Trig::asin( ($panel->{'ampy'}->GetValue - $amp)/$amp );
-	my $drot = $panel->{'rota'}->GetValue / 2000;
-	my $rot_richtung = $panel->{'rot_richtung'}->GetValue();
-	$drot = - $drot if $rot_richtung eq 'rechts';
-	my $friction = 1 - 0.000001 * $panel->{'friction'}->GetValue;
-	my $duration = 4000 * $panel->{'length'}->GetValue;
-	my $zoomfaktor = 1.2**$panel->{'zoom'}->GetValue;
-	my $colormorph = $panel->{'color'}->GetValue/1000;
-	my $color = 0;
-	my $rot = 0;
-	my $cur_amplit = $amp;
-	for my $cc (0 .. $duration) {
-		$color += $colormorph;
-		$color = 0 if $color > 1536;
-		my ($r, $g, $b) = (0,0,0);
-		if    ($color <  256){ $r = 255; $g = $color;}
-		elsif ($color <  512){ $g = 255; $r = 511  - $color;}
-		elsif ($color <  768){ $g = 255; $b = $color-511;}
-		elsif ($color < 1024){ $b = 255; $g = 1023 - $color;}
-		elsif ($color < 1280){ $b = 255; $r = $color - 1023;}
-		elsif ($color < 1535){ $r = 255; $b = 1534 - $color;}
-		$app->{'dc'}->SetPen( Wx::Pen->new( Wx::Colour->new( $r, $g, $b ), 1, &Wx::wxSOLID) );
-		my $xs = sin($x += $dx);
-		my $ys = sin($y += $dy);
-		$cur_amplit *= $friction;
-		$rot += $drot;
-		($xs, $ys) = rotate($xs, $ys, $rot) unless $rot_richtung eq 'keine';
-		$app->{'dc'}->DrawPoint( ($xs*$cur_amplit*$zoomfaktor)+$midoffset,
-		                       ($ys*$cur_amplit*$zoomfaktor)+$midoffset );
-	}
-	$app->{'maltafel'}->SetBitmap( $app->{'bmp'} );
-	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-	$app->{'maltafel'}->Refresh();
-}
-
-sub rotate {
-	my ($x, $y, $r) = @_;
-	my ($sinr, $cosr) = (sin($r), cos($r));
-	return ($cosr*$x - $sinr*$y, $sinr*$x + $cosr*$y);
-}
-
-package Wx::Perl::Box;
-use base qw(Wx::Panel);
-sub new {
-	my ($class, $parent, $label, $widgets) = @_;
-	return unless ref $widgets eq 'ARRAY';
-	my ($self) = $class->SUPER::new( $parent );
-	my ($sizer) = Wx::StaticBoxSizer->new 
-			(Wx::StaticBox->new($self, -1, " $label "), &Wx::wxVERTICAL);
-
-	for my $widget (@$widgets) {
-		next unless ref $widget and $widget->isa('Wx::Control');
-		$sizer->Add( $widget, 0, &Wx::wxLEFT, 0); # 10
-		$widget->Reparent( $self );
-	}
-	$self->SetSizerAndFit( $sizer );
-	$self;
-}
-
-
-package main;
-Harmonograph->new->MainLoop;

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

View file
  • Ignore whitespace
 package Wx::Perl::Smart::Frame;
 use base qw(Wx::Frame);
 use Scalar::Util qw(blessed looks_like_number);
-#sub new {
-	#my ($class, $parent, $widgets, $border) = @_;
-	#return unless ref $widgets eq 'ARRAY';
 
-	#my ($self)  = $class->SUPER::new( $parent );
-	#my $callback = sub { $_[0]->Reparent($self) if $_[0]->isa('Wx::Window') };
-	#$self->{'sizer'} = Wx::Perl::Sizer->new($widgets, undef, $border, $callback);
-	#$self->SetSizerAndFit( $self->{'sizer'} );
-	#$self;
-#}
+#sub new { #my ($class, $parent, $widgets, $border) = @_; #$self; #}
 
 sub SubscribeWidgets {
 	my ($self, $widgets) = @_;
-	#my $widgets = shift
-	#for my $widget
-	#die unless 
+	warn ((caller(0))[3].'needs a hashref as parameter') unless ref $widgets eq 'HASH';
+	my ($name, $widget);
+	while (($name, $widget) = each %$widgets) {
+		warn ((caller(0))[3].": $name is already subscribed"), next if $self->{'subwidgets'}{$name};
+		warn ((caller(0))[3].": $widget is not a widget") unless blessed($widget) and $widget->isa('Wx::Window');
+		$self->{'subwidgets'}{$name} = $widget;
+		$widget->Show(0);
+	 }
+}
+sub UnsubscribeWidgets {
+	my ($self) = shift;
+	for my $name (@_){
+		warn ((caller(0))[3].": no widget subscribed as $name") unless $self->{'subwidgets'}{$name};
+		$self->{'subwidgets'}{$name} = '';
+	}
+}
+sub GetSubscribedWidget {
+	my ($self) = shift;
+	my @widget;
+	for my $name (@_){
+		warn ((caller(0))[3].": there is no widget subscribed as $name") unless $self->{'subwidgets'}{$name};
+		push @widget, $self->{'subwidgets'}{$name};
+	}
+	return $widget[0] if scalar @widget == 1;
+	return @widget;
+}
+sub w { shift->GetSubscribedWidget(@_) }
+#sub WidgetSubscribed {shift->{'subwidgets'}{shift;}}
+
+
+sub CreateSubWidgets {
+	my ($self, $widgets) = @_;
+	
 }
 
 sub SetSmartLayout {
 	my ($self) = shift;
-#say $self->GetParent;# 
 
 	$self->SetSizerAndFit( 
-		Wx::Perl::Smart::Sizer->new(undef, [ 
+		Wx::Perl::Smart::Sizer->new($self, [ 
 			Wx::Perl::Smart::Panel->new( $self, [ @_ ] ) 
 		])
 	);

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

View file
  • Ignore whitespace
 	for (keys %default_arg) { $arg{$_} = defined $init_arg->{$_} ? $init_arg->{$_} : $default_arg{$_} }
 	$arg{'flags'} = ${$arg{'flags'}} | $default_arg{'flags'} if ref $arg{'flags'} eq 'SCALAR';
 	$arg{'add_callback'} = $default_arg{'add_callback'} unless ref $arg{'add_callback'} eq 'CODE';
+	my $daddy = $parent;
+	$daddy = $daddy->GetParent while $daddy->GetParent;
+	$daddy = '' unless ref $daddy eq 'Wx::Perl::Smart::Frame' or ref $daddy eq 'Wx::Perl::Smart::Panel';
 
 	my ($self) = $class->SUPER::new( $orientation );
 	for (my $pos = 0; $pos < scalar @$widgets; $pos++) {
 						{add_callback => $arg{'add_callback'}}
 					);
 				}
-
+			}
+			elsif (substr($widget, 0, 1) eq '<' and substr($widget, -1) eq '>') {
+				next unless $daddy;
+				$widget = $daddy->GetSubscribedWidget( substr($widget, 1, -1) );
+				next unless $widget;
 			}
 			elsif (looks_like_number($widget)) {
 				$proportion 
 		}
 		next unless blessed($widget) and ($widget->isa('Wx::Window') or $widget->isa('Wx::Sizer'));
 		$arg{'add_callback'}->($widget);
+		$widget->Reparent($parent) if defined $parent and $widget->isa('Wx::Window');
+		$widget->Show(1) if $widget->isa('Wx::Window');
 		$self->Add( $widget, $proportion, $arg{'flags'}, $arg{'border'});
-		$widget->Reparent($parent) if defined $parent and $widget->isa('Wx::Window');
 	}
 	return $self;
 }
 
 Syntax Def:
 
+$var		Widget Ref			pointer to the self made widget object/string/number/list
+<name>		named widget		these have to subscribe to the smart frame/panel first
+
 'text'		Label				creates a static text widget
 '---'		Line				creates a static line widget
-								(horizontal or vertical according to sizer orient)
+'|'								(horizontal or vertical according to sizer orient)
 								(use as many '-' or '|' as you prefer)
 number		n px space			inserts a spacer with that number amount of pixel
 \...		proportion++		all space left gets divided between the hungry according their proportion number
 								(default is 0)
 \n			stretching space	creates a hungry spacer (more backslashes => more proportion)
+
 [,,]		BoxSizer			member widgets get stacked vertically, in subarray horizontally, and so on
-%box =>[]	StaticBoxSizer		arrange widgets within a labeled box
-%grid=>[]	GridBoxSizer		arrange widgets within a table
+%LabeledBox	StaticBoxSizer		arrange widgets within a labeled box
+%TabbedBox	Notebook			arrange widgets within tabs of a notebook
+%Grid=>[]	GridBoxSizer		arrange widgets within a table
 {k=>v,}		set attributes		overwrite selected attributs, become effective with next element

File lib/favs.yml

View file
  • Ignore whitespace
   thickness: 2
   y_invers: ''
   zoom: 0
-sonne:
-  amplitude_x: 270
-  amplitude_y: 270
-  density: 90
-  flow_colour: 1
-  frequency_x: 5
-  frequency_y: 4
-  friction: 5
-  length: 46
-  rotation: 1
-  rotation_dir: Links
-  scale_colour: 1
-  start_colour: 0
-  thickness: 1
-  y_invers: ''
-  zoom: '-1'
 sonnenrad:
   amp_x: 265
   amp_y: 265

File lib/harmonograph.pl

View file
  • Ignore whitespace
-#!/usr/bin/perl
-use v5.12;
-use warnings;
-use Wx;
-use Wx::Perl::Smart::Frame;
-use Colour::Flow;
-use Math::Trig;
-use File::Spec;
-use YAML::Tiny;
+#!/usr/bin/perl
+use v5.12;
+use warnings;
+use Wx;
+use Wx::Perl::Smart::Frame;
+use Colour::Flow;
+use Math::Trig;
+use File::Spec;
+use YAML::Tiny;
+
+package Harmonograph;
+our $VERSION = '0.89';
+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;
 
-package Harmonograph;
-our $VERSION = '0.89';
-use base qw(Wx::App);
+	# 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}};
+
+	my $frame = $app->{'frame'} = Wx::Perl::Smart::Frame->new(
+		undef, -1, __PACKAGE__." $VERSION", [1,1],[-1,-1],
+		&Wx::wxCAPTION | &Wx::wxCLOSE_BOX | &Wx::wxFRAME_TOOL_WINDOW | &Wx::wxSYSTEM_MENU #| &Wx::wxRESIZE_BORDER
+	);
+	Wx::InitAllImageHandlers();
+	$frame->SetIcon( Wx::GetWxPerlIcon() );
+
+	$midoffset = $boardsize / 2;
+	$max_amp = $midoffset - 10;   # max amplitude
+	%range_defaults = ( # label, min, max, init
+		frequency_x => ['X',               1, 1,  27], frequency_y => ['Y',              1,  1,   27],
+		amplitude_x => ['X', $max_amp, 0, $max_amp*2], amplitude_y => ['Y',       $max_amp,  0,$max_amp*2],
+		rotation    => [$l18n{'amount'},   1, 0,  20], 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],
+		scale_colour=> [$l18n{'scale'},    1, 1,   4],
+	);
 
-my $maltafelgroesse = 560;
-my (%l18n, %range_defaults, $midoffset, $max_amp, );
-my $fav_file = 'favs.yml'; 
-my $l18n_file = 'localisation.yml';
-my $language = 'german';
-
-sub OnInit {
-	my $app = shift;
-	die "$l18n_file is missing " unless -e $l18n_file;
-	%l18n = %{YAML::Tiny->read( $l18n_file )->[0]{$language}};
-
-	my $frame = $app->{'frame'} = Wx::Perl::Smart::Frame->new(
-		undef, -1, __PACKAGE__." $VERSION", [1,1],[700,-1],
-		&Wx::wxCAPTION | &Wx::wxCLOSE_BOX | &Wx::wxFRAME_TOOL_WINDOW | &Wx::wxSYSTEM_MENU #| &Wx::wxRESIZE_BORDER
-	);
-	Wx::InitAllImageHandlers();
-	$frame->SetIcon( Wx::GetWxPerlIcon() );
-
-	$midoffset = $maltafelgroesse / 2;
-	$max_amp = $midoffset - 10;   # max amplitude
-	%range_defaults = ( # label, min, max, init
-		frequency_x => ['X',               1, 1,  27], frequency_y => ['Y',              1,  1,   27],
-		amplitude_x => ['X', $max_amp, 0, $max_amp*2], amplitude_y => ['Y',       $max_amp,  0,$max_amp*2],
-		rotation    => [$l18n{'amount'},   1, 0,  20], 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],
-		scale_colour=> [$l18n{'scale'},    1, 1,   4],
-	);
-
-	$app->{'maltafel'} = Wx::StaticBitmap->new($frame, -1, &Wx::wxNullBitmap);
-	$app->{'maltafel'}->SetMinSize([$maltafelgroesse, $maltafelgroesse]);
-	$app->{'bmp'}   = Wx::Bitmap->new( $maltafelgroesse, $maltafelgroesse);
-	$app->{'dc'}  = Wx::MemoryDC->new();
+	$frame->SubscribeWidgets({
+		drawboard => Wx::StaticBitmap->new($frame, -1, &Wx::wxNullBitmap)
+	});
+	$frame->w('drawboard')->SetMinSize([$boardsize, $boardsize]);
+	$app->{'bmp'}   = Wx::Bitmap->new( $boardsize, $boardsize);
+	$app->{'dc'}  = Wx::MemoryDC->new();
 	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-
-	my (%button);
-	my $callback = sub { $app->Repaint() };
-	$app->{$_} = Wx::Perl::TextSlider->new
-		($frame, @{$range_defaults{$_}}, $callback) for keys %range_defaults;
-	$app->{'y_invers'} = Wx::CheckBox->new($frame, -1, $l18n{'y_inverse'});
+
+	my (%button);
+	my $callback = sub { $app->Repaint() };
+	$app->{$_} = Wx::Perl::TextSlider->new
+		($frame, @{$range_defaults{$_}}, $callback) for keys %range_defaults;
+	$app->{'y_invers'} = Wx::CheckBox->new($frame, -1, $l18n{'y_inverse'});
 	Wx::Event::EVT_CHECKBOX( $app->{'y_invers'}, -1, $callback );
-	$app->{'rotation_dir'} = Wx::Perl::RadioGroup->new(
-		$frame, $l18n{'no'}, [$l18n{'no'}, $l18n{'left'}, $l18n{'right'}],
-		&Wx::wxHORIZONTAL, $callback
-	);
-	$button{$_} = Wx::Button->new($frame, -1, $l18n{$_}) for qw(save all remember forget);
-	Wx::Event::EVT_BUTTON( $button{'save'},    $button{'save'},    sub {$app->Save()    } );
-	Wx::Event::EVT_BUTTON( $button{'all'},     $button{'all'},     sub {$app->SaveAll() } );
-	Wx::Event::EVT_BUTTON( $button{'remember'},$button{'remember'},sub {$app->Remember()} );
-	Wx::Event::EVT_BUTTON( $button{'forget'},  $button{'forget'},  sub {$app->Forget()  } );
-
-	my ($fsaved, @saved, ) = ( '', ());
-	$app->{'favorites'} = -e $fav_file ? YAML::Tiny->read( $fav_file ) : YAML::Tiny->new;
-	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->SetValues( $app->{'favorites'}[0]{ $app->{'fav_select'}->GetValue() } );
+	$app->{'rotation_dir'} = Wx::Perl::RadioGroup->new(
+		$frame, $l18n{'no'}, [$l18n{'no'}, $l18n{'left'}, $l18n{'right'}],
+		&Wx::wxHORIZONTAL, $callback
+	);
+	$button{$_} = Wx::Button->new($frame, -1, $l18n{$_}) for qw(save all remember forget);
+	Wx::Event::EVT_BUTTON( $button{'save'},    $button{'save'},    sub {$app->Save()    } );
+	Wx::Event::EVT_BUTTON( $button{'all'},     $button{'all'},     sub {$app->SaveAll() } );
+	Wx::Event::EVT_BUTTON( $button{'remember'},$button{'remember'},sub {$app->Remember()} );
+	Wx::Event::EVT_BUTTON( $button{'forget'},  $button{'forget'},  sub {$app->Forget()  } );
+
+	my ($fsaved, @saved, ) = ( '', ());
+	$app->{'favorites'} = -e $fav_file ? YAML::Tiny->read( $fav_file ) : YAML::Tiny->new;
+	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->SetValues( $app->{'favorites'}[0]{ $app->{'fav_select'}->GetValue() } );
 	});
-
-	$frame->SetSmartLayout(
-		[   # left part
-			$app->{'maltafel'},
-			10,
-			{border => 10, flags => &Wx::wxALL|&Wx::wxGROW},
-			$app->{'fav_select'},
-			[
-				$button{'save'},
-				{border => 20, flags => &Wx::wxLEFT},
-				$button{'all'},
-				\1,
-				{border => 20, flags => &Wx::wxRIGHT},
-				$button{'forget'}, 
-				{border => 0},
-				$button{'remember'}
-			],
-		],[ # right half
-			-TabbedBox =>  [ 
-				$l18n{'oscillators'} => [[
-					{border => 5, flags => &Wx::wxGROW|&Wx::wxALL},
-					-LabeledBox => [ $l18n{'frequency'} => [$app->{'frequency_x'}, $app->{'frequency_y'}, $app->{'y_invers'}]],
-					-LabeledBox => [ $l18n{'start_amp'} => [$app->{'amplitude_x'}, $app->{'amplitude_y'}  ]],
-					-LabeledBox => [ $l18n{'rotation'}  => [$app->{'rotation_dir'},$app->{'rotation'}     ]],
-					{border => 10},
-					$app->{'friction'}, 
-				]],
-				$l18n{'visuals'} => [[
-					{border => 5, flags => &Wx::wxGROW|&Wx::wxALL},
-					-LabeledBox => [ $l18n{'line'}  => [$app->{'length'},      $app->{'density'},    $app->{'thickness'}   ]],
-					-LabeledBox => [ $l18n{'color'} => [$app->{'start_colour'},$app->{'flow_colour'},$app->{'scale_colour'}]],
-					{border => 10},
-					$app->{'zoom'},
-				]]
-			],
-		],
-	);
-	$app->ResetValues('all');
-	$app->Repaint();
-	$frame->Center();
-	$frame->Show(1);
-	$app->SetTopWindow($frame);
-	1;
-}
-sub OnExit {
-	my $app = shift;
-	$app->{'favorites'}->write( $fav_file );
-	1;
-}
-
-
-sub SubscribeWidget {
-	my $app = shift;
-}
-
-sub GetValues {
-	my $app = shift;
-	my %value;
-	$value{$_} = $app->{$_}->GetValue() for (keys %range_defaults, qw(rotation_dir y_invers));
-	return \%value;
-}
-sub SetValues {
-	my $app = shift;
-	my %value = %{shift;};
-	$app->{$_}->SetValue( $value{$_} ) for (keys %range_defaults, qw(rotation_dir y_invers));
-	$app->Repaint();
-}
-sub ResetValues { shift->ResetValue }     # for naming consistency
-sub ResetValue {
-	my $app = shift;
-	my $which = shift // 'all';
-	if ($which eq 'all'){
-		$app->{'y_invers'}->SetValue(0);
-		$app->ResetValue($_) for (keys %range_defaults, 'rotation_dir');
-	} 
-	else { $app->{$which}->ResetValue }
-	$app;
-}
-
-
-
-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;
-	$app->{'favorites'}[0]{$name} = $app->GetValues;
-	$app->{'fav_select'}->SetValue($name);
-	$app->{'fav_select'}->Insert($name, 0);
-	$app->{'favorites'}->write( $fav_file );
-}
-sub Forget {
-	my $app = shift;
-	my $cb = $app->{'fav_select'};
-	return if $cb->IsEmpty;
-	my $name = $cb->GetValue();
-	$cb->Delete( $cb->FindString($name) );
-	delete $app->{'favorites'}[0]{$name};
-	if ($cb->IsEmpty){
-		$cb->SetValue('');
-		$app->ResetValues();
-		$app->{'favorites'} =  YAML::Tiny->new;
-	} else {
-		$cb->SetSelection(0);
-		$app->SetValues( $app->{'favorites'}[0]{ $cb->GetValue() } );
-		delete $app->{'favorites'}[0]{$name};
-	}
-	$app->Repaint();
-	$app->{'favorites'}->write( $fav_file );
-}
-
-
-
-sub Save {
-	my $app = shift;
-	my $file = Wx::FileSelector(
-		$l18n{'save_under'}, '.', $app->{'fav_select'}->GetValue().'.jpg',
-		'', '(*)|*', &Wx::wxFD_SAVE, $app->{'frame'}
-	);
-	return unless $file;
-	my $img = Wx::Image->new( $app->{'maltafel'}->GetBitmap() );  # in later versions use: ->ConvertToImage() 
-	$img->SaveFile($file, &Wx::wxBITMAP_TYPE_JPEG ); #wxBITMAP_TYPE_BMP
-}
-sub SaveAll {
-	my $app = shift;
-	my $dir = Wx::DirSelector( $l18n{'save_all_under'}, '.', 0, [-1,-1], $app->{'frame'});
-	return unless -d $dir;
-	my $values = $app->GetValues();
-	for (keys $app->{'favorites'}[0]) {
-		$app->SetValues( $app->{'favorites'}[0]{$_} );
-		my $file = File::Spec->catfile($dir, $_.'.jpg' );
-		my $img = Wx::Image->new( $app->{'maltafel'}->GetBitmap() );  # in later versions use: ->ConvertToImage() 
-		$img->SaveFile($file, &Wx::wxBITMAP_TYPE_JPEG ); #wxBITMAP_TYPE_BMP
-	}
-	$app->SetValues($values);
-	$app->Repaint();
-}
-
-
-
-sub Repaint {
-	my $app = shift;
-	my $value = $app->GetValues;
-	#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 eq $l18n{'right'};
-	my $friction = 1 - 0.000001 * $value->{'friction'};
-	$value->{'zoom'} = 1.2**$value->{'zoom'};
-	my $rot = 0;
-	my $cur_amp = $max_amp;
-	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 );
-
-	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) unless $rota_dir eq $l18n{'no'};
-		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->{'maltafel'}->SetBitmap( $app->{'bmp'} );
-	$app->{'dc'}->SelectObject( $app->{'bmp'} );
-	$app->{'maltafel'}->Refresh();
-}
-sub rotate {
-	my ($x, $y, $r) = @_;
-	my ($sinr, $cosr) = (sin($r), cos($r));
-	return ($cosr*$x - $sinr*$y, $sinr*$x + $cosr*$y);
-}
-
-package main;
-Harmonograph->new->MainLoop;
+	$frame->SubscribeWidgets({
+		save            => $button{'save'},
+		save_all        => $button{'all'},
+		forget          => $button{'forget'},
+		remember        => $button{'remember'},
+		check_y_invers  => $app->{'y_invers'},
+		fav_select_box  => $app->{'fav_select'},
+	});
+
+
+	$frame->SetSmartLayout(
+		[   # left part
+			'<drawboard>',
+			10,
+			{border => 10, flags => &Wx::wxALL|&Wx::wxGROW},
+			'<fav_select_box>',
+			['<save>', 20, '<save_all>',\1, '<forget>', 20, '<remember>'],
+		],[ # right half
+			-TabbedBox =>  [ 
+				$l18n{'oscillators'} => [[
+					{border => 5, flags => &Wx::wxGROW|&Wx::wxALL},
+					-LabeledBox => [ $l18n{'frequency'} => [$app->{'frequency_x'}, 
+															$app->{'frequency_y'},
+															'<check_y_invers>'     ]],
+					-LabeledBox => [ $l18n{'start_amp'} => [$app->{'amplitude_x'},
+															$app->{'amplitude_y'}  ]],
+					-LabeledBox => [ $l18n{'rotation'}  => [$app->{'rotation_dir'},
+															$app->{'rotation'}     ]],
+					{border => 10},							$app->{'friction'}, 
+				]],
+				$l18n{'visuals'} => [[
+					{border => 5, flags => &Wx::wxGROW|&Wx::wxALL},
+					-LabeledBox => [ $l18n{'line'}  => [$app->{'length'},
+														$app->{'density'},
+														$app->{'thickness'}   ]],
+					-LabeledBox => [ $l18n{'color'} => [$app->{'start_colour'},
+														$app->{'flow_colour'},
+														$app->{'scale_colour'}]],
+					{border => 10},						$app->{'zoom'},
+				]]
+			],
+		],
+	);
+	$app->ResetValues('all');
+	$app->Repaint();
+	$frame->Center();
+	$frame->Show(1);
+	$app->SetTopWindow($frame);
+	1;
+}
+sub OnExit {
+	my $app = shift;
+	$app->{'favorites'}->write( $fav_file );
+	1;
+}
+
+
+sub SubscribeWidget {
+	my $app = shift;
+}
+
+sub GetValues {
+	my $app = shift;
+	my %value;
+	$value{$_} = $app->{$_}->GetValue() for (keys %range_defaults, qw(rotation_dir y_invers));
+	return \%value;
+}
+sub SetValues {
+	my $app = shift;
+	my %value = %{shift;};
+	$app->{$_}->SetValue( $value{$_} ) for (keys %range_defaults, qw(rotation_dir y_invers));
+	$app->Repaint();
+}
+sub ResetValues { shift->ResetValue }     # for naming consistency
+sub ResetValue {
+	my $app = shift;
+	my $which = shift // 'all';
+	if ($which eq 'all'){
+		$app->{'y_invers'}->SetValue(0);
+		$app->ResetValue($_) for (keys %range_defaults, 'rotation_dir');
+	} 
+	else { $app->{$which}->ResetValue }
+	$app;
+}
+
+
+
+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;
+	$app->{'favorites'}[0]{$name} = $app->GetValues;
+	$app->{'fav_select'}->SetValue($name);
+	$app->{'fav_select'}->Insert($name, 0);
+	$app->{'favorites'}->write( $fav_file );
+}
+sub Forget {
+	my $app = shift;
+	my $cb = $app->{'fav_select'};
+	return if $cb->IsEmpty;
+	my $name = $cb->GetValue();
+	$cb->Delete( $cb->FindString($name) );
+	delete $app->{'favorites'}[0]{$name};
+	if ($cb->IsEmpty){
+		$cb->SetValue('');
+		$app->ResetValues();
+		$app->{'favorites'} =  YAML::Tiny->new;
+	} else {
+		$cb->SetSelection(0);
+		$app->SetValues( $app->{'favorites'}[0]{ $cb->GetValue() } );
+		delete $app->{'favorites'}[0]{$name};
+	}
+	$app->Repaint();
+	$app->{'favorites'}->write( $fav_file );
+}
+
+
+
+sub Save {
+	my $app = shift;
+	my $file = Wx::FileSelector(
+		$l18n{'save_under'}, '.', $app->{'fav_select'}->GetValue().'.jpg',
+		'', '(*)|*', &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
+}
+sub SaveAll {
+	my $app = shift;
+	my $dir = Wx::DirSelector( $l18n{'save_all_under'}, '.', 0, [-1,-1], $app->{'frame'});
+	return unless -d $dir;
+	my $values = $app->GetValues();
+	for (keys $app->{'favorites'}[0]) {
+		$app->SetValues( $app->{'favorites'}[0]{$_} );
+		my $file = File::Spec->catfile($dir, $_.'.jpg' );
+		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->SetValues($values);
+	$app->Repaint();
+}
+
+
+
+sub Repaint {
+	my $app = shift;
+	my $value = $app->GetValues;
+	#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 eq $l18n{'right'};
+	my $friction = 1 - 0.000001 * $value->{'friction'};
+	$value->{'zoom'} = 1.2**$value->{'zoom'};
+	my $rot = 0;
+	my $cur_amp = $max_amp;
+	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 );
+
+	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) unless $rota_dir eq $l18n{'no'};
+		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();
+}
+sub rotate {
+	my ($x, $y, $r) = @_;
+	my ($sinr, $cosr) = (sin($r), cos($r));
+	return ($cosr*$x - $sinr*$y, $sinr*$x + $cosr*$y);
+}
+
+package main;
+Harmonograph->new->MainLoop;

File lib/localisation.yaml

View file
  • Ignore whitespace
+---
+english:
+  all: All
+  amount: Amount
+  ask_another_name: 'Please select another name or delete the other memory first.'
+  ask_for_a_name: 'Remember under which name ?'
+  color: Color
+  density: Density
+  line: Line
+  flow: Flow
+  forget: Forget
+  frequency: Frequency
+  friction: Friction
+  left: Left
+  length: Length
+  no: 'No'
+  oscillators: Oscillators
+  remember: Remember
+  right: Right
+  rotation: Rotation
+  save: 'Save'
+  save_all_under: 'Save All Remebered as JPG Files in Directory ...'
+  save_under: 'Save Picture as JPG File under ...'
+  scale: Scale
+  start: Start
+  start_amp: 'Starting Amplitude'
+  thickness: Thickness
+  visuals: Visuals
+  y_inverse: 'Y - Inverse Direction'
+  zoom: Zoom
+german:
+  all: Alle
+  amount: Betrag
+  ask_another_name: 'Bitte anderen Namen waehlen, oder Vorhandenes vorher loeschen.'
+  ask_for_a_name: 'Unter welchem Namen merken ?'
+  color: Farbe
+  density: Dichte
+  line: Linie
+  flow: Verlauf
+  forget: Vergiss
+  frequency: Frequenz
+  friction: Reibung
+  left: Links
+  length: Laenge
+  thickness: Dicke
+  no: Keine
+  oscillators: Oszillatoren
+  remember: Merke
+  right: Rechts
+  rotation: Rotation
+  save: 'Speichern'
+  save_all_under: 'Speicher alle Gemerkten als JPG im Verzeichnis ...'
+  save_under: 'Speichere Bild als JPG Datei unter ...'
+  scale: Skala
+  start: Start
+  start_amp: Startamplitude
+  visuals: Optik
+  y_inverse: 'Y - Richtung invers'
+  zoom: Zoom

File lib/localisation.yml

  • Ignore whitespace
----
-english:
-  all: All
-  amount: Amount
-  ask_another_name: 'Please select another name or delete the other memory first.'
-  ask_for_a_name: 'Remember under which name ?'
-  color: Color
-  density: Density
-  line: Line
-  flow: Flow
-  forget: Forget
-  frequency: Frequency
-  friction: Friction
-  left: Left
-  length: Length
-  no: 'No'
-  oscillators: Oscillators
-  remember: Remember
-  right: Right
-  rotation: Rotation
-  save: 'Save'
-  save_all_under: 'Save All Remebered as JPG Files in Directory ...'
-  save_under: 'Save Picture as JPG File under ...'
-  scale: Scale
-  start: Start
-  start_amp: 'Starting Amplitude'
-  thickness: Thickness
-  visuals: Visuals
-  y_inverse: 'Y - Inverse Direction'
-  zoom: Zoom
-german:
-  all: Alle
-  amount: Betrag
-  ask_another_name: 'Bitte anderen Namen waehlen, oder Vorhandenes vorher loeschen.'
-  ask_for_a_name: 'Unter welchem Namen merken ?'
-  color: Farbe
-  density: Dichte
-  line: Linie
-  flow: Verlauf
-  forget: Vergiss
-  frequency: Frequenz
-  friction: Reibung
-  left: Links
-  length: Laenge
-  thickness: Dicke
-  no: Keine
-  oscillators: Oszillatoren
-  remember: Merke
-  right: Rechts
-  rotation: Rotation
-  save: 'Speichern'
-  save_all_under: 'Speicher alle Gemerkten als JPG im Verzeichnis ...'
-  save_under: 'Speichere Bild als JPG Datei unter ...'
-  scale: Skala
-  start: Start
-  start_amp: Startamplitude
-  visuals: Optik
-  y_inverse: 'Y - Richtung invers'
-  zoom: Zoom