#=============================================== # TkDataConvert #=============================================== package TkDataConvert; use clib::TkFittingCommon; @ISA = qw(TkFittingCommon); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; use Math::Spline; use Utils; use IniFile; use CSV; use MultiColumnData; use MyTk::GraphFrameArray; use Sci::Science; use Sci::Algorism; use Sci::Optimize; #============================================================ # 変数等取得関数 #============================================================ #============================================================ # コンストラクタ、デストラクタ #============================================================ #呼び出されることはない sub new { my ($module, $app) = @_; my $this = {}; bless $this; return $this; } sub DESTROY { my $this = shift; $this->SUPER::DESTROY(@_); } #============================================================ # 継承クラスで定義しなおす関数 #============================================================ sub CreateLeftFrame { my ($this, $ConfigSide, @args) = @_; # return $this->SUPER::CreateLeftFrame(); return undef; } sub CreateSelectFilePane { my ($this) = @_; # return $this->SUPER::CreateSelectFilePane(); return undef; } sub CreateFileContentPane { return undef; } sub CreateWidgets { my ($this, $DoSuperOnly) = @_; $DoSuperOnly = 0 if(!defined $DoSuperOnly); my $App = $this->App(); my $args = $App->Args(); my $mw = $this->mw(); $this->{ini} = new IniFile; $this->{DataFile} = new MultiColumnData; $this->SUPER::CreateWidgets(); return if($DoSuperOnly); $mw->SetTitle("DataConvert2007 / TkPlot"); $this->App()->SetProgram("DataConvert2007 / TkPlot"); #=================================================== # ペインを作製 #=================================================== my $EntryWidth = 12; my $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->MakeChooseFileEntry($Frame, 'SampleCSV', 'Data:', $this->{ini}->pVariable("SampleCSVFile", ""), '&Choose', sub { $this->ChooseFile(@_); }, "ChooseSampleFile"); $this->{EditDataButton} = $Frame->MyButton( -text => 'ed', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'EditSampleCSV'); }, )->pack(-side => 'left'); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); my $lsb1 = $this->{ChooseXListBox} = $Frame->MyBrowseEntry( -label => "X:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb1->configure(-browsecmd => sub { $this->SelChangeListBox("X", $this->{ChooseXListBox}); } ); my $lsb2 = $this->{ChooseY1ListBox} = $Frame->MyBrowseEntry( -label => "Y1:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb2->configure(-browsecmd => sub { $this->SelChangeListBox("Y1", $this->{ChooseY1ListBox}); } ); $Frame = $mw->MyLabFrame( -label => 'Action', -labelside => 'acrosstop', )->pack(-anchor => 'nw', -fill => 'x'); my $lsb4 = $this->{ChooseActionListBox} = $Frame->MyBrowseEntry( -label => "Action:", -state => "readonly", -takefocus => 1, -width => 10, -Selections => [ 'mLSQ', 'InterpolateByMath::Spline', 'InterpolateByCubicSpline', 'InterpolateBy4thPolynomial', 'InterpolateBySimpson', 'InterpolateByLagrange', 'Interpolate', 'IntegrateByRectangle', 'IntegrateByTrapezoid', 'IntegrateBySimpson', 'IntegrateByBode', # 'IntegrateByCubicSpline', 'DifferentiateByLinearAverage', 'DifferentiateByLagrange', 'DifferentiateByCubicSpline', 'SmoothingBySimpleAverage', 'AdaptiveSmoothing', 'SmoothingBymLSQ', ], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb4->configure(-browsecmd => sub { $this->SelChangeListBox("Direction", $this->{ChooseActionListBox}); } ); $Frame = $mw->MyLabFrame( -label => 'Output', -labelside => 'acrosstop', )->pack(-anchor => 'nw', -fill => 'x'); $this->MakeLabelEntry($Frame, "X0", "X:", $this->{ini}->pVariable('X0', 0.0), "%g", $EntryWidth, "", 0, 0); $this->MakeLabelEntry($Frame, "X1", "-", $this->{ini}->pVariable('X1', 0.0), "%g", $EntryWidth, "", 0, 1); $this->MakeLabelEntry($Frame, "XStep", ",", $this->{ini}->pVariable('XStep', 0.05), "%g", $EntryWidth, "", 0, 2); $Frame = $mw->MyLabFrame( -label => 'mLSQ', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "m", "m:", $this->{ini}->pVariable('m', 3), "%d", $EntryWidth, "", 0, 0); $Frame = $mw->MyLabFrame( -label => 'smoothing', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "nSmooth", "n:", $this->{ini}->pVariable('nSmooth', 5), "%d", $EntryWidth, "", 0, 0); $Frame = $mw->MyLabFrame( -label => 'common', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $Frame->MyCheckbutton( -text => "Sort data", -variable => $this->{ini}->pVariable('SortData', 1), -onvalue => 1, -offvalue => 0, )->pack(-anchor => 'nw'); $Frame = $mw->MyLabFrame( -label => 'Find X', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "Y", "Y:", $this->{ini}->pVariable('Y', 1.0e-7), "%g", $EntryWidth, "", 0, 0); $this->{FindYButton} = $Frame->MyButton( -text => 'Find &X', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'FindY'); }, )->grid(-row => 0, -column => 4, -columnspan => 1, -sticky => 'e'); $this->UpdateParameters(); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{RecalcButton} = $Frame->MyButton( -text => '&Recalc', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'Recalc'); }, )->pack(-side => 'left'); #ツールバーのOpenボタンをファイル読み込みにバインドする $mw->{OpenButton}->configure( -command => sub { $this->ChooseFile($this->{SampleCSVEntry}); }, ) if($mw->{OpenButton}); } sub Initialize { my ($this, $InitializeParameterFile, $InitializeData) = @_; $this->{DataFile} = new MultiColumnData; if($InitializeParameterFile) { $this->{ini}->{SampleCSVFile} = ''; } if($InitializeData) { $this->{pCalX} = undef; $this->{pCalY1} = undef; $this->{pCalY2} = undef; $this->{FindX} = undef; $this->{FindY} = undef; } } sub SelChangeListBox { my ($this, $type, $lb) = @_; #print "this:$this lb=$lb\n"; my $s = $lb->GetText(); if($type eq 'X' or $type eq 'Y1') { } $this->AssignGraphData(); $this->Draw(); } sub ButtonPressed { my ($this, $event, $type) = @_; my $App = $this->App(); #print "this=$this\n"; if($type eq 'Recalc') { $this->Recalc(); } elsif($type eq 'FindY') { $this->FindY(); } elsif($type eq 'SaveParam') { $this->SaveParameterFile($this->{ini}->{ParameterFile}); return; } elsif($type eq 'EditParam') { my $Editor = $this->App()->{EditorPath}; my $File = $this->{ini}->{ParameterFile}; my $command = "$Editor $File"; system($command); return; } elsif($type eq 'EditSampleCSV') { my $Editor = $this->App()->{EditorPath}; my $File = $this->{ini}->{SampleCSVFile}; my $command = "$Editor $File"; system($command); return; } elsif($type eq 'EditSubstrateCSV') { my $Editor = $this->App()->{EditorPath}; my $File = $this->{ini}->{SubstrateCSVFile}; my $command = "$Editor $File"; system($command); return; } } #=================================================== # データ処理 #=================================================== sub FindY { my ($this) = @_; my $pX = $this->{CalX}; my $pY1 = $this->{CalY1}; if(!defined $pY1) { $this->App()->print("Error: Can not get the calculated data.\n"); return; } my $Data = new MultiColumnData; #print "X=$pX, $pY1\n"; $Data->SetXYData($pX, $pY1); my ($xmin, $xmax) = $Data->GetXMinMax(); #print "xm=$xmin,$xmax\n"; my $absxmax = abs($xmax); $absxmax = abs($xmin) if($absxmax < abs($xmin)); my $y = $this->{ini}->{Y}; $this->App()->print("Find x for y=$y\n"); my $xf = $Data->XVal($y, $absxmax * 1.0e-6); my $yf = $Data->YVal($xf); $this->App()->print("x=$xf, y=$yf\n"); $this->{FindX} = [$xf]; $this->{FindY} = [$yf]; $this->AssignGraphData(0); $this->Draw(); } sub Recalc { my ($this, $ResetViewRange) = @_; $ResetViewRange = 1 if(!defined $ResetViewRange); my ($pX, $pY1) = ($this->{DataFile}->pX(), $this->{DataFile}->pY()); my $nData = @$pX; if($this->{ini}->{SortData}) { my $pSortedData = $this->{DataFile}->SortXYByX(1); $pX = $pSortedData->pX(); $pY1 = $pSortedData->pY(); $nData = $pSortedData->nData(); } my $YLabel = ''; my $Action = $this->{ChooseActionListBox}->GetText(); my $x0 = $this->{ini}->{X0}; my $x1 = abs($this->{ini}->{X1}); my $xstep = abs($this->{ini}->{XStep}); print "Action: $Action [x=$x0 - $x1, $xstep]\n"; $this->{iTargetGraphFrame} = 0; if($Action eq 'mLSQ') { my $optimize = new Optimize(); my @c = $optimize->mlsq($pX, $pY1, $this->{ini}->{m}); $optimize->PrintParameters(); my (@x, @y); for(my $i = 0 ; ; $i++) { my $xx = $x0 + $xstep * $i; last if($xx > $x1); my $yy = $optimize->YCal($xx); $x[$i] = $xx; $y[$i] = $yy; } $this->{CalX} = \@x; $this->{CalY1} = \@y; } elsif($Action eq 'DifferentiateByLinearAverage' or $Action eq 'IntegrateByRectangle' or $Action eq 'IntegrateByTrapezoid' or $Action eq 'IntegrateBySimpson') { $this->{CalX} = $pX; if($Action eq 'DifferentiateByLinearAverage') { $this->{CalY1} = Algorism::DifferentiateByLinearAverage($pX, $pY1); } elsif($Action eq 'IntegrateByRectangle') { $this->{CalY1} = Algorism::IntegrateByRectangle($pX, $pY1); } elsif($Action eq 'IntegrateByTrapezoid') { $this->{CalY1} = Algorism::IntegrateByTrapezoid($pX, $pY1); } elsif($Action eq 'IntegrateBySimpson') { $this->{CalY1} = Algorism::IntegrateBySimpson($pX, $pY1); } elsif($Action eq 'IntegrateByBode') { $this->{CalY1} = Algorism::IntegrateByBode($pX, $pY1); } else { $this->App()->print("Error in Recalc:: Invalid Action [$Action] #1.\n"); return undef; } $this->{iTargetGraphFrame} = 1; } elsif($Action eq 'IntegrateByCubicSpline') { } elsif($Action eq'InterpolateByMath::Spline') { my $spline=new Math::Spline($pX, $pY1); my (@x, @y); my $c = 0; for(my $i = 0 ; ; $i++) { my $xx = $x0 + $xstep * $i; last if($xx > $x1); my $yy = $spline->evaluate($xx); next if(!defined $yy); $x[$c] = $xx; $y[$c] = $yy; $c++; } $this->{CalX} = \@x; $this->{CalY1} = \@y; if($Action =~ /Interpolate/i) { $this->{iTargetGraphFrame} = 0; } else { $this->{iTargetGraphFrame} = 1; } } elsif($Action =~ /Interpolate/i or $Action =~ /Differentiate/i) { my $func; if($Action eq 'Interpolate') { $func = \&Algorism::Interpolate; } elsif($Action eq 'InterpolateBySimpson') { $func = \&Algorism::InterpolateBySimpson; } elsif($Action eq 'InterpolateBy4thPolynomial') { $func = \&Algorism::InterpolateBy4thPolynomial; } elsif($Action eq 'InterpolateByLagrange') { $func = \&Algorism::InterpolateByLagrange; } elsif($Action eq 'InterpolateByCubicSpline') { $func = \&Algorism::InterpolateByCubicSpline; } elsif($Action eq 'DifferentiateByLagrange') { $func = \&Algorism::DifferentiateByLagrange; } elsif($Action eq 'DifferentiateByCubicSpline') { $func = \&Algorism::DifferentiateByCubicSpline; } else { $this->App()->print("Error in Recalc:: Invalid Action [$Action] #2.\n"); return undef; } my (@x, @y); my $c = 0; for(my $i = 0 ; ; $i++) { my $xx = $x0 + $xstep * $i; last if($xx > $x1); my $yy = &$func($pX, $pY1, $xx); next if(!defined $yy); $x[$c] = $xx; $y[$c] = $yy; $c++; } $this->{CalX} = \@x; $this->{CalY1} = \@y; if($Action =~ /Interpolate/i) { $this->{iTargetGraphFrame} = 0; } else { $this->{iTargetGraphFrame} = 1; } } elsif($Action eq'SmoothingBySimpleAverage') { $this->{CalX} = $pX; $this->{CalY1} = Algorism::SmoothingBySimpleAverage($pY1, $this->{ini}->{nSmooth}); $this->{iTargetGraphFrame} = 0; } elsif($Action eq'AdaptiveSmoothing') { $this->{CalX} = $pX; $this->{CalY1} = Algorism::AdaptiveSmoothing($pY1, $this->{ini}->{nSmooth}); $this->{iTargetGraphFrame} = 0; } elsif($Action eq'SmoothingBymLSQ') { my $m = $this->{ini}->{m}; my $n = $this->{ini}->{nSmooth}; my $nData = @$pY1; my (@x, @y); my $c = 0; my $optimize = new Optimize(); for(my $i = 0 ; $i < $nData ; $i++) { my $j0 = $i - int($m / 2); if($n/2 < 0) { $j0 = 0; } elsif($j0 + $n > $nData) { $j0 = $nData - $n - 2; } my (@xx, @yy); for(my $k = 0 ; $k < $n ; $k++) { $xx[$k] = $pX->[$j0 + $k]; $yy[$k] = $pY1->[$j0 + $k]; } $optimize->mlsq(\@xx, \@yy, $m); $x[$c] = $pX->[$i]; $y[$c] = $optimize->YCal($pX->[$i]); $c++; } $this->{CalX} = \@x; $this->{CalY1} = \@y; } else { $this->App()->print("Error in Recalc:: Invalid Action [$Action] #3.\n"); return; } $this->AssignGraphData(0); if($this->{iTargetGraphFrame} >= 1) { my $GraphFrameArray = $this->GetGraphFrameArray(); my $GraphFrame1 = $GraphFrameArray->GetGraphFrame(1); $GraphFrame1->CalMinMax(); $GraphFrame1->AdjustViewRange(0.05, 0.05, 0.05, 0.05); } $this->Draw(); } sub ReadSampleDataFile { my ($this, $filepath, $IsPrint) = @_; my $App = $this->App(); #print "type=$type\n"; $this->Initialize(0, 1); $this->{Path} = $filepath; $this->{DataFile}->SetSkipBlankData(1); my ($nData, $pLabelArray, @pDataArray) = $this->{DataFile}->Read($filepath); if(!defined $nData) { $App->print("Error: Can not read [$filepath].\n"); return $this->{Path} = undef; } my $nDataArray = $this->{DataFile}->nDataArray(); #print "nDataArray: $nDataArray\n"; $this->{"ChooseXListBox"}->DeleteAllItem(); $this->{"ChooseY1ListBox"}->DeleteAllItem(); for(my $i = 0 ; $i < $nDataArray ; $i++) { #print "$i: $pLabelArray->[$i]\n"; $this->{"ChooseXListBox"}->AddItem($pLabelArray->[$i]); $this->{"ChooseY1ListBox"}->AddItem($pLabelArray->[$i]); } $this->{"ChooseXListBox"}->SetCurSel(0); $this->{"ChooseY1ListBox"}->SetCurSel(1); my $pX = $this->{DataFile}->GetXData(0); my $pY1 = $this->{DataFile}->GetYData(1); ($this->{ini}->{X0}, $this->{ini}->{X1}) = $this->{DataFile}->CalMinMax(); my ($minxstep, $maxstep) = $this->{DataFile}->CalMinMaxStep(); my $n = 0; if($minxstep > 0.0) { $n = int( abs($this->{ini}->{X1} - $this->{ini}->{X0}) / $minxstep + 1.0001); } if($n > 101 or $minxstep == 0.0) { $minxstep = abs($this->{ini}->{X1} - $this->{ini}->{X0}) / 101; } $this->{ini}->{XStep} = $minxstep; $this->CreateGraphFrame(); $this->AssignGraphData(); return $filepath; } sub CreateGraphFrame { my ($this, $canvas, $TargetData) = @_; $canvas = $this->Canvas(); my $App = $this->App(); my $font = $App->{'GraphFrameFont'}; my @font = split(/,/, $font) if($font); my $w = $canvas->width(); my $h = $canvas->height(); # my $FileType = $this->FileType(); # return undef unless($FileType); # my $pDataArray = $this->DataArray(); # return unless($pDataArray); my $GraphFrameArray = $this->{'GraphFrameArray'} = new GraphFrameArray($this->mw()); $GraphFrameArray->SetCanvasSize($w, $h); $GraphFrameArray->AddGraphFrame(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); my $FramePosStr0 = $App->{"GraphFrame0Position"}; $GraphFrame0->SetPositionByStr($FramePosStr0); $GraphFrameArray->AddGraphFrame(); my $GraphFrame1 = $GraphFrameArray->GetGraphFrame(1); my $FramePosStr1 = $App->{"GraphFrame1Position"}; $GraphFrame1->SetPositionByStr($FramePosStr1); my $XScale0 = $GraphFrame0->GetXScale(0); my $YScale0 = $GraphFrame0->GetYScale(0); $XScale0->SetScaleStringVisible(1); $XScale0->SetCaptionVisible(1); $GraphFrame0->SetViewRange(0, 0, 1, 1); $GraphFrame1->SetViewRange(0, 0, 1, 1); } sub AssignGraphData { my ($this, $ResetViewRange) = @_; $ResetViewRange = 1 if(!defined $ResetViewRange); my $GraphFrameArray = $this->GetGraphFrameArray(); my @GraphFrame; $GraphFrame[0] = $GraphFrameArray->GetGraphFrame(0); $GraphFrame[1] = $GraphFrameArray->GetGraphFrame(1); $GraphFrame[0]->ClearAllData(); $GraphFrame[1]->ClearAllData(); my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); $GraphFrame[0]->SetXCaption($XLabel); $GraphFrame[0]->SetYCaption("$Y1Label"); my $pX = $this->{DataFile}->GetXData($XLabel) if($XLabel); my $pY1 = $this->{DataFile}->GetYData($Y1Label) if($Y1Label); my $nData = $this->{DataFile}->nData(); if($pX) { $GraphFrame[0]->AddGraphData($pX, $pY1, 1, "black", "", 6, "red", 0, "red", "XAutoSkip", "$XLabel", "${Y1Label}") if($pY1); } my $iTarget = $this->{iTargetGraphFrame}; my $pCalX = $this->{CalX}; my $pCalY1 = $this->{CalY1}; if($pCalX) { $GraphFrame[$iTarget]->AddGraphData($pCalX, $pCalY1, 0, "red", "circle", 5, "", 1, "red", "XAutoSkip", "$XLabel", "${Y1Label}(cal)") if($pCalY1); if($iTarget >= 1) { $GraphFrame[$iTarget]->SetXCaption($XLabel); $GraphFrame[$iTarget]->SetYCaption("${Y1Label}(cal)"); } } my $pFindX = $this->{FindX}; my $pFindY = $this->{FindY}; if($pFindX) { $GraphFrame[$iTarget]->AddGraphData($pFindX, $pFindY, 0, "red", "square", 8, "", 1, "red", "XAutoSkip", "$XLabel", "${Y1Label}(FindX)") if($pFindY); if($iTarget >= 1) { $GraphFrame[$iTarget]->SetXCaption($XLabel); $GraphFrame[$iTarget]->SetYCaption("${Y1Label}(cal)"); } } $GraphFrame[0]->SetYScalePlotType('x'); $GraphFrame[1]->SetYScalePlotType('x'); if($ResetViewRange) { $GraphFrame[0]->CalMinMax(); $GraphFrame[0]->AdjustViewRange(0.05, 0.05, 0.05, 0.05); $GraphFrame[1]->CalMinMax(); $GraphFrame[1]->AdjustViewRange(0.05, 0.05, 0.05, 0.05); } } 1;