KiCad PCB EDA Suite
sim_plot_frame.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2016 CERN
5  * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
7  * @author Maciej Suminski <maciej.suminski@cern.ch>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 3
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * https://www.gnu.org/licenses/gpl-3.0.html
22  * or you may search the http://www.gnu.org website for the version 3 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #include <wx/stc/stc.h>
28 
29 #include <sch_edit_frame.h>
30 #include <eeschema_id.h>
31 #include <kiway.h>
32 #include <confirm.h>
33 #include <bitmaps.h>
35 #include <widgets/tuner_slider.h>
38 #include <pgm_base.h>
39 #include "sim_plot_colors.h"
40 #include "sim_plot_frame.h"
41 #include "sim_plot_panel.h"
42 #include "spice_simulator.h"
43 #include "spice_reporter.h"
44 #include <menus_helpers.h>
45 #include <tool/tool_manager.h>
46 #include <tools/ee_actions.h>
47 #include <eeschema_settings.h>
48 #include <wx/ffile.h>
49 #include <dialog_shim.h>
50 
52 {
53  int res = (int) aFirst | (int) aSecond;
54 
55  return (SIM_PLOT_TYPE) res;
56 }
57 
58 
60 {
61 public:
63  : m_parent( aParent )
64  {
65  }
66 
67  REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
68  {
69  wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
70  event->SetString( aText );
71  wxQueueEvent( m_parent, event );
72  return *this;
73  }
74 
75  bool HasMessage() const override
76  {
77  return false; // Technically "indeterminate" rather than false.
78  }
79 
80  void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
81  {
82  wxCommandEvent* event = NULL;
83 
84  switch( aNewState )
85  {
86  case SIM_IDLE:
87  event = new wxCommandEvent( EVT_SIM_FINISHED );
88  break;
89 
90  case SIM_RUNNING:
91  event = new wxCommandEvent( EVT_SIM_STARTED );
92  break;
93 
94  default:
95  wxFAIL;
96  return;
97  }
98 
99  wxQueueEvent( m_parent, event );
100  }
101 
102 private:
104 };
105 
106 
107 TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName,
108  SIM_PLOT_TYPE aType, const wxString& aParam )
109  : m_name( aName ), m_type( aType ), m_param( aParam )
110 {
111  // Title generation
112  m_title = wxString::Format( "%s(%s)", aParam, aName );
113 
114  if( aType & SPT_AC_MAG )
115  m_title += " (mag)";
116  else if( aType & SPT_AC_PHASE )
117  m_title += " (phase)";
118 }
119 
120 // Store the path of saved workbooks during the session
122 
123 SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent )
124  : SIM_PLOT_FRAME_BASE( aParent ),
125  m_lastSimPlot( nullptr ),
126  m_welcomePanel( nullptr ),
127  m_plotNumber( 0 )
128 {
129  SetKiway( this, aKiway );
131 
133 
134  if( m_schematicFrame == NULL )
135  throw std::runtime_error( "There is no schematic window" );
136 
137  // Give an icon
138  wxIcon icon;
139  icon.CopyFromBitmap( KiBitmap( simulator_xpm ) );
140  SetIcon( icon );
141 
142  // Get the previous size and position of windows:
143  LoadSettings( config() );
144 
145  // Prepare the color list to plot traces
147 
148  // Give icons to menuitems
150 
152 
153  if( !m_simulator )
154  {
155  throw std::runtime_error( "Could not create simulator instance" );
156  return;
157  }
158 
159  m_simulator->Init();
160 
161  if( m_savedWorkbooksPath.IsEmpty() )
162  {
164  }
165 
166  m_reporter = new SIM_THREAD_REPORTER( this );
167  m_simulator->SetReporter( m_reporter );
168 
169  // the settings dialog will be created later, on demand.
170  // if created in the ctor, for some obscure reason, there is an issue
171  // on Windows: when open it, the simulator frame is sent to the background.
172  // instead of being behind the dialog frame (as it does)
173  m_settingsDlg = nullptr;
174 
176 
177  Connect( EVT_SIM_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onSimUpdate ), NULL, this );
178  Connect( EVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this );
179  Connect( EVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this );
180  Connect( EVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this );
181  Connect( EVT_SIM_CURSOR_UPDATE, wxCommandEventHandler( SIM_PLOT_FRAME::onCursorUpdate ), NULL, this );
182 
183  // Toolbar buttons
184  m_toolSimulate = m_toolBar->AddTool( ID_SIM_RUN, _( "Run/Stop Simulation" ),
185  KiBitmap( sim_run_xpm ), _( "Run Simulation" ), wxITEM_NORMAL );
186  m_toolAddSignals = m_toolBar->AddTool( ID_SIM_ADD_SIGNALS, _( "Add Signals" ),
187  KiBitmap( sim_add_signal_xpm ), _( "Add signals to plot" ), wxITEM_NORMAL );
188  m_toolProbe = m_toolBar->AddTool( ID_SIM_PROBE, _( "Probe" ),
189  KiBitmap( sim_probe_xpm ), _( "Probe signals on the schematic" ), wxITEM_NORMAL );
190  m_toolTune = m_toolBar->AddTool( ID_SIM_TUNE, _( "Tune" ),
191  KiBitmap( sim_tune_xpm ), _( "Tune component values" ), wxITEM_NORMAL );
192  m_toolSettings = m_toolBar->AddTool( wxID_ANY, _( "Sim Parameters" ),
193  KiBitmap( config_xpm ), _( "Simulation parameters and settings" ), wxITEM_NORMAL );
194 
195  Connect( m_toolSimulate->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
196  wxCommandEventHandler( SIM_PLOT_FRAME::onSimulate ), NULL, this );
197  Connect( m_toolAddSignals->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
198  wxCommandEventHandler( SIM_PLOT_FRAME::onAddSignal ), NULL, this );
199  Connect( m_toolProbe->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
200  wxCommandEventHandler( SIM_PLOT_FRAME::onProbe ), NULL, this );
201  Connect( m_toolTune->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
202  wxCommandEventHandler( SIM_PLOT_FRAME::onTune ), NULL, this );
203  Connect( m_toolSettings->GetId(), wxEVT_COMMAND_TOOL_CLICKED,
204  wxCommandEventHandler( SIM_PLOT_FRAME::onSettings ), NULL, this );
205 
206  // Bind toolbar buttons event to existing menu event handlers, so they behave the same
207  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSimulate, this, m_runSimulation->GetId() );
208  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onAddSignal, this, m_addSignals->GetId() );
209  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onProbe, this, m_probeSignals->GetId() );
210  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onTune, this, m_tuneValue->GetId() );
211  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onShowNetlist, this, m_showNetlist->GetId() );
212  Bind( wxEVT_COMMAND_MENU_SELECTED, &SIM_PLOT_FRAME::onSettings, this, m_settings->GetId() );
213 
214  m_toolBar->Realize();
215 
216  m_welcomePanel = new SIM_PANEL_BASE( wxEmptyString, m_plotNotebook, wxID_ANY );
217  m_plotNotebook->AddPage( m_welcomePanel, _( "Welcome!" ), 1, true );
218 
219  // resize the subwindows size. At least on Windows, calling wxSafeYield before
220  // resizing the subwindows forces the wxSplitWindows size events automatically generated
221  // by wxWidgets to be executed before our resize code.
222  // Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
223  // events
224  wxSafeYield();
226 
227  // Ensure the window is on top
228  Raise();
229 }
230 
231 
233 {
234  m_simulator->SetReporter( nullptr );
235  delete m_reporter;
236  delete m_signalsIconColorList;
237 
238  if( m_settingsDlg )
239  m_settingsDlg->Destroy();
240 }
241 
242 
244 {
245  EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
246  wxASSERT( cfg );
247 
248  if( cfg )
249  {
251 
252  cfg->m_Simulator.plot_panel_width = m_splitterLeftRight->GetSashPosition();
253  cfg->m_Simulator.plot_panel_height = m_splitterPlotAndConsole->GetSashPosition();
254  cfg->m_Simulator.signal_panel_height = m_splitterSignals->GetSashPosition();
255  cfg->m_Simulator.cursors_panel_height = m_splitterTuneValues->GetSashPosition();
257  }
258 }
259 
260 
262 {
263  EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
264  wxASSERT( cfg );
265 
266  if( cfg )
267  {
269 
270  // Read subwindows sizes (should be > 0 )
276  }
277 }
278 
279 
281 {
282  EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
283  wxASSERT( cfg );
284 
285  return cfg ? &cfg->m_Simulator.window : nullptr;
286 }
287 
288 
289 // A small helper struct to handle bitmaps initialisation in menus
291 {
292  int m_MenuId;
294 };
295 
296 
298 {
299  // Give icons to menuitems of the main menubar
300  BM_MENU_INIT_ITEM bm_list[]
301  {
302  // File menu:
303  { wxID_NEW, simulator_xpm },
304  { wxID_OPEN, directory_open_xpm },
305  { wxID_SAVE, save_xpm},
308  { wxID_CLOSE, exit_xpm},
309 
310  // simulator menu:
317 
318  // View menu
319  { wxID_ZOOM_IN, zoom_in_xpm},
320  { wxID_ZOOM_OUT, zoom_out_xpm},
321  { wxID_ZOOM_FIT, zoom_fit_in_page_xpm},
326 
327  { 0, nullptr } // Sentinel
328  };
329 
330  // wxMenuItems are already created and attached to the m_mainMenu wxMenuBar.
331  // A problem is the fact setting bitmaps in wxMenuItems after they are attached
332  // to a wxMenu do not work in all cases.
333  // So the trick is:
334  // Remove the wxMenuItem from its wxMenu
335  // Set the bitmap
336  // Insert the modified wxMenuItem to its previous place
337  for( int ii = 0; bm_list[ii].m_MenuId; ++ii )
338  {
339  wxMenuItem* item = m_mainMenu->FindItem( bm_list[ii].m_MenuId );
340 
341  if( !item || !bm_list[ii].m_Bitmap)
342  continue;
343 
344  wxMenu* menu = item->GetMenu();
345  // Calculate the initial index of item inside the wxMenu parent
346  wxMenuItemList& mlist = menu->GetMenuItems();
347  int mpos = mlist.IndexOf( item );
348 
349  if( mpos >= 0 ) // Should be always the case
350  {
351  // Modify the bitmap
352  menu->Remove( item );
353  AddBitmapToMenuItem( item, KiBitmap( bm_list[ii].m_Bitmap ) );
354  // Insert item to its the initial index
355  menu->Insert( mpos, item );
356  }
357  }
358 }
359 
360 
362 {
365 
368 
371 
374 }
375 
376 
377 void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand )
378 {
379  STRING_FORMATTER formatter;
380 
381  if( !m_settingsDlg )
382  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
383 
384  m_simConsole->Clear();
386 
387  if( aSimCommand.IsEmpty() )
388  {
389  SIM_PANEL_BASE* plotPanel = currentPlotWindow();
390 
391  if( plotPanel && m_plots.count( plotPanel ) != 0 )
392  m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand );
393  }
394  else
395  {
396  m_exporter->SetSimCommand( aSimCommand );
397  }
398 
399  if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) )
400  {
401  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
402  return;
403  }
404 
405  if( m_exporter->GetSimType() == ST_UNKNOWN )
406  {
407  DisplayInfoMessage( this, _( "You need to select the simulation settings first." ) );
408  return;
409  }
410 
411  m_simulator->LoadNetlist( formatter.GetString() );
412  updateTuners();
413  applyTuners();
414  m_simulator->Run();
415 }
416 
417 
419 {
420  m_simulator->Stop();
421 }
422 
423 
425 {
426  return m_simulator ? m_simulator->IsRunning() : false;
427 }
428 
429 
431 {
432  SIM_PANEL_BASE* plotPanel;
434 
435  if( SIM_PANEL_BASE::IsPlottable( simType ) )
436  {
437  SIM_PLOT_PANEL* panel;
438  panel = new SIM_PLOT_PANEL( aSimCommand, m_plotNotebook, this, wxID_ANY );
439 
441  Pgm().GetCommonSettings()->m_Input.scroll_modifier_zoom != 0 );
442 
443  plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
444  }
445  else
446  {
447  SIM_NOPLOT_PANEL* panel;
448  panel = new SIM_NOPLOT_PANEL( aSimCommand, m_plotNotebook, wxID_ANY );
449  plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
450  }
451 
452  if( m_welcomePanel )
453  {
454  m_plotNotebook->DeletePage( 0 );
455  m_welcomePanel = nullptr;
456  }
457 
458  wxString pageTitle( m_simulator->TypeToName( simType, true ) );
459  pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) );
460 
461  m_plotNotebook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true );
462  m_plots[plotPanel] = PLOT_INFO();
463 
464  return plotPanel;
465 }
466 
467 
468 void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
469 {
470  addPlot( aNetName, SPT_VOLTAGE, "V" );
471 }
472 
473 
474 void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName, const wxString& aParam )
475 {
476  addPlot( aDeviceName, SPT_CURRENT, aParam );
477 }
478 
479 
481 {
482  SIM_PANEL_BASE* plotPanel = currentPlotWindow();
483 
484  if( !plotPanel || plotPanel == m_welcomePanel )
485  return;
486 
487  // For now limit the tuner tool to RLC components
488  char primitiveType = NETLIST_EXPORTER_PSPICE::GetSpiceField( SF_PRIMITIVE, aComponent, 0 )[0];
489 
490  if( primitiveType != SP_RESISTOR && primitiveType != SP_CAPACITOR && primitiveType != SP_INDUCTOR )
491  return;
492 
493  const wxString componentName = aComponent->GetField( REFERENCE_FIELD )->GetText();
494 
495  // Do not add multiple instances for the same component
496  auto tunerIt = std::find_if( m_tuners.begin(), m_tuners.end(), [&]( const TUNER_SLIDER* t )
497  {
498  return t->GetComponentName() == componentName;
499  }
500  );
501 
502  if( tunerIt != m_tuners.end() )
503  return; // We already have it
504 
505  try
506  {
507  TUNER_SLIDER* tuner = new TUNER_SLIDER( this, m_tunePanel, aComponent );
508  m_tuneSizer->Add( tuner );
509  m_tuners.push_back( tuner );
510  m_tunePanel->Layout();
511  }
512  catch( const KI_PARAM_ERROR& e )
513  {
514  // Sorry, no bonus
515  DisplayError( nullptr, e.What() );
516  }
517 }
518 
519 
520 void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
521 {
522  if( aErase )
523  m_tuners.remove( aTuner );
524 
525  aTuner->Destroy();
526  m_tunePanel->Layout();
527 }
528 
529 
531 {
532  SIM_PANEL_BASE* curPage = currentPlotWindow();
533 
534  return ( ( !curPage || curPage->GetType() == ST_UNKNOWN ) ?
535  nullptr :
536  dynamic_cast<SIM_PLOT_PANEL*>( curPage ) );
537 }
538 
539 
541 {
542  return m_exporter.get();
543 }
544 
545 
546 void SIM_PLOT_FRAME::addPlot( const wxString& aName, SIM_PLOT_TYPE aType, const wxString& aParam )
547 {
548  SIM_TYPE simType = m_exporter->GetSimType();
549 
550  if( simType == ST_UNKNOWN )
551  {
552  m_simConsole->AppendText( _( "Error: simulation type not defined!\n" ) );
553  m_simConsole->SetInsertionPointEnd();
554  return;
555  }
556  else if( !SIM_PANEL_BASE::IsPlottable( simType ) )
557  {
558  m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting!\n" ) );
559  m_simConsole->SetInsertionPointEnd();
560  return;
561  }
562 
563  // Create a new plot if the current one displays a different type
564  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
565 
566  if( !plotPanel || plotPanel->GetType() != simType )
567  plotPanel =
568  dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( m_exporter->GetUsedSimCommand() ) );
569 
570  wxASSERT( plotPanel );
571 
572  if( !plotPanel ) // Something is wrong
573  return;
574 
575  TRACE_DESC descriptor( *m_exporter, aName, aType, aParam );
576 
577  bool updated = false;
578  SIM_PLOT_TYPE xAxisType = GetXAxisType( simType );
579 
580  if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
581  {
582  int baseType = descriptor.GetType() & ~( SPT_AC_MAG | SPT_AC_PHASE );
583 
584  // Add two plots: magnitude & phase
585  TRACE_DESC mag_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_MAG ) );
586  TRACE_DESC phase_desc( *m_exporter, descriptor, (SIM_PLOT_TYPE)( baseType | SPT_AC_PHASE ) );
587 
588  updated |= updatePlot( mag_desc, plotPanel );
589  updated |= updatePlot( phase_desc, plotPanel );
590  }
591  else
592  {
593  updated = updatePlot( descriptor, plotPanel );
594  }
595 
596  if( updated )
597  {
599  }
600 }
601 
602 
603 void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase )
604 {
605  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
606 
607  if( !plotPanel )
608  return;
609 
610  if( aErase )
611  {
612  auto& traceMap = m_plots[plotPanel].m_traces;
613  auto traceIt = traceMap.find( aPlotName );
614  wxASSERT( traceIt != traceMap.end() );
615  traceMap.erase( traceIt );
616  }
617 
618  wxASSERT( plotPanel->TraceShown( aPlotName ) );
619  plotPanel->DeleteTrace( aPlotName );
620  plotPanel->GetPlotWin()->Fit();
621 
623  wxCommandEvent dummy;
625 }
626 
627 
629 {
631  if( m_settingsDlg )
633 }
634 
635 
636 bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* aPanel )
637 {
638  SIM_TYPE simType = m_exporter->GetSimType();
639  wxString spiceVector = m_exporter->ComponentToVector(
640  aDescriptor.GetName(), aDescriptor.GetType(), aDescriptor.GetParam() );
641 
642  if( !SIM_PANEL_BASE::IsPlottable( simType ) )
643  {
644  // There is no plot to be shown
645  m_simulator->Command( wxString::Format( "print %s", spiceVector ).ToStdString() );
646 
647  return false;
648  }
649 
650  // First, handle the x axis
651  wxString xAxisName( m_simulator->GetXAxis( simType ) );
652 
653  if( xAxisName.IsEmpty() )
654  return false;
655 
656  auto data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
657  unsigned int size = data_x.size();
658 
659  if( data_x.empty() )
660  return false;
661 
662  SIM_PLOT_TYPE plotType = aDescriptor.GetType();
663  std::vector<double> data_y;
664 
665  // Now, Y axis data
666  switch( m_exporter->GetSimType() )
667  {
668  case ST_AC:
669  {
670  wxASSERT_MSG( !( ( plotType & SPT_AC_MAG ) && ( plotType & SPT_AC_PHASE ) ),
671  "Cannot set both AC_PHASE and AC_MAG bits" );
672 
673  if( plotType & SPT_AC_MAG )
674  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
675  else if( plotType & SPT_AC_PHASE )
676  data_y = m_simulator->GetPhasePlot( (const char*) spiceVector.c_str() );
677  else
678  wxASSERT_MSG( false, "Plot type missing AC_PHASE or AC_MAG bit" );
679  }
680  break;
681 
682  case ST_NOISE:
683  case ST_DC:
684  case ST_TRANSIENT:
685  {
686  data_y = m_simulator->GetMagPlot( (const char*) spiceVector.c_str() );
687  }
688  break;
689 
690  default:
691  wxASSERT_MSG( false, "Unhandled plot type" );
692  return false;
693  }
694 
695  if( data_y.size() != size )
696  return false;
697 
698  // If we did a two-source DC analysis, we need to split the resulting vector and add traces
699  // for each input step
700  SPICE_DC_PARAMS source1, source2;
701 
702  if( m_exporter->GetSimType() == ST_DC &&
703  m_exporter->ParseDCCommand( m_exporter->GetUsedSimCommand(), &source1, &source2 ) )
704  {
705  if( !source2.m_source.IsEmpty() )
706  {
707  // Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step
708  SPICE_VALUE v = source2.m_vstart;
709  wxString name;
710 
711  size_t offset = 0;
712  size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
713  size_t inner = data_x.size() / ( outer + 1 );
714 
715  wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
716 
717  for( size_t idx = 0; idx <= outer; idx++ )
718  {
719  name = wxString::Format( "%s (%s = %s V)", aDescriptor.GetTitle(),
720  source2.m_source, v.ToString() );
721 
722  std::vector<double> sub_x( data_x.begin() + offset,
723  data_x.begin() + offset + inner );
724  std::vector<double> sub_y( data_y.begin() + offset,
725  data_y.begin() + offset + inner );
726 
727  if( aPanel->AddTrace( name, inner,
728  sub_x.data(), sub_y.data(), aDescriptor.GetType() ) )
729  {
730  m_plots[aPanel].m_traces.insert( std::make_pair( name, aDescriptor ) );
731  }
732 
733  v = v + source2.m_vincrement;
734  offset += inner;
735  }
736 
737  return true;
738  }
739  }
740 
741  if( aPanel->AddTrace( aDescriptor.GetTitle(), size,
742  data_x.data(), data_y.data(), aDescriptor.GetType() ) )
743  {
744  m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) );
745  }
746 
747  return true;
748 }
749 
750 
752 {
753  m_signals->ClearAll();
754 
755  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
756 
757  if( !plotPanel )
758  return;
759 
760  wxSize size = m_signals->GetClientSize();
761  m_signals->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x );
762 
763  // Build an image list, to show the color of the corresponding trace
764  // in the plot panel
765  // This image list is used for trace and cursor lists
766  wxMemoryDC bmDC;
767  const int isize = bmDC.GetCharHeight();
768 
770  m_signalsIconColorList = new wxImageList( isize, isize, false );
771  else
772  m_signalsIconColorList->RemoveAll();
773 
774  for( const auto& trace : CurrentPlot()->GetTraces() )
775  {
776  wxBitmap bitmap( isize, isize );
777  bmDC.SelectObject( bitmap );
778  wxColour tcolor = trace.second->GetPen().GetColour();
779 
780  wxColour bgColor = m_signals->wxWindow::GetBackgroundColour();
781  bmDC.SetPen( wxPen( bgColor ) );
782  bmDC.SetBrush( wxBrush( bgColor ) );
783  bmDC.DrawRectangle( 0, 0, isize, isize ); // because bmDC.Clear() does not work in wxGTK
784 
785  bmDC.SetPen( wxPen( tcolor ) );
786  bmDC.SetBrush( wxBrush( tcolor ) );
787  bmDC.DrawRectangle( 0, isize / 4 + 1, isize, isize / 2 );
788 
789  bmDC.SelectObject( wxNullBitmap ); // Needed to initialize bitmap
790 
791  bitmap.SetMask( new wxMask( bitmap, *wxBLACK ) );
792  m_signalsIconColorList->Add( bitmap );
793  }
794 
795  if( bmDC.IsOk() )
796  {
797  bmDC.SetBrush( wxNullBrush );
798  bmDC.SetPen( wxNullPen );
799  }
800 
801  m_signals->SetImageList( m_signalsIconColorList, wxIMAGE_LIST_SMALL );
802 
803  // Fill the signals listctrl. Keep the order of names and
804  // the order of icon color identical, because the icons
805  // are also used in cursor list, and the color index is
806  // calculated from the trace name index
807  int imgidx = 0;
808 
809  for( const auto& trace : m_plots[plotPanel].m_traces )
810  {
811  m_signals->InsertItem( imgidx, trace.first, imgidx );
812  imgidx++;
813  }
814 }
815 
816 
818 {
819  const auto& spiceItems = m_exporter->GetSpiceItems();
820 
821  for( auto it = m_tuners.begin(); it != m_tuners.end(); /* iteration inside the loop */ )
822  {
823  const wxString& ref = (*it)->GetComponentName();
824 
825  if( std::find_if( spiceItems.begin(), spiceItems.end(), [&]( const SPICE_ITEM& item )
826  {
827  return item.m_refName == ref;
828  }) == spiceItems.end() )
829  {
830  // The component does not exist anymore, remove the associated tuner
831  TUNER_SLIDER* tuner = *it;
832  it = m_tuners.erase( it );
833  RemoveTuner( tuner, false );
834  }
835  else
836  {
837  ++it;
838  }
839  }
840 }
841 
842 
844 {
845  for( auto& tuner : m_tuners )
846  {
848  std::string command( "alter @" + tuner->GetSpiceName()
849  + "=" + tuner->GetValue().ToSpiceString() );
850 
851  m_simulator->Command( command );
852  }
853 }
854 
855 
856 bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath )
857 {
858  m_plots.clear();
859  m_plotNotebook->DeleteAllPages();
860 
861  wxTextFile file( aPath );
862 
863  if( !file.Open() )
864  return false;
865 
866  long plotsCount;
867 
868  if( !file.GetFirstLine().ToLong( &plotsCount ) ) // GetFirstLine instead of GetNextLine
869  return false;
870 
871  for( long i = 0; i < plotsCount; ++i )
872  {
873  long plotType, tracesCount;
874 
875  if( !file.GetNextLine().ToLong( &plotType ) )
876  return false;
877 
878  wxString simCommand = file.GetNextLine();
879  SIM_PANEL_BASE* plotPanel = NewPlotPanel( simCommand );
880  m_plots[plotPanel].m_simCommand = simCommand;
881  StartSimulation( m_plots[plotPanel].m_simCommand );
882 
883  // Perform simulation, so plots can be added with values
884  do
885  {
886  wxThread::This()->Sleep( 50 );
887  }
888  while( IsSimulationRunning() );
889 
890  if( !file.GetNextLine().ToLong( &tracesCount ) )
891  return false;
892 
893  for( long j = 0; j < tracesCount; ++j )
894  {
895  long traceType;
896  wxString name, param;
897 
898  if( !file.GetNextLine().ToLong( &traceType ) )
899  return false;
900 
901  name = file.GetNextLine();
902  param = file.GetNextLine();
903 
904  if( name.IsEmpty() || param.IsEmpty() )
905  return false;
906 
907  addPlot( name, (SIM_PLOT_TYPE) traceType, param );
908  }
909  }
910 
911  return true;
912 }
913 
914 
915 bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath )
916 {
917  wxString savePath = aPath;
918 
919  if( !savePath.Lower().EndsWith(".wbk") )
920  savePath += ".wbk";
921 
922  wxTextFile file( savePath );
923 
924  if( file.Exists() )
925  {
926  if( !file.Open() )
927  return false;
928 
929  file.Clear();
930  }
931  else
932  {
933  file.Create();
934  }
935 
936  file.AddLine( wxString::Format( "%llu", m_plots.size() ) );
937 
938  for( const auto& plot : m_plots )
939  {
940  if( plot.first )
941  {
942  file.AddLine( wxString::Format( "%d", plot.first->GetType() ) );
943  file.AddLine( plot.second.m_simCommand );
944  file.AddLine( wxString::Format( "%llu", plot.second.m_traces.size() ) );
945 
946  for( const auto& trace : plot.second.m_traces )
947  {
948  file.AddLine( wxString::Format( "%d", trace.second.GetType() ) );
949  file.AddLine( trace.second.GetName() );
950  file.AddLine( trace.second.GetParam() );
951  }
952  }
953  }
954 
955  bool res = file.Write();
956  file.Close();
957 
958  return res;
959 }
960 
961 
963 {
964  switch( aType )
965  {
966  case ST_AC:
967  return SPT_LIN_FREQUENCY;
969 
970  case ST_DC:
971  return SPT_SWEEP;
972 
973  case ST_TRANSIENT:
974  return SPT_TIME;
975 
976  default:
977  wxASSERT_MSG( false, "Unhandled simulation type" );
978  return (SIM_PLOT_TYPE) 0;
979  }
980 }
981 
982 
983 void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent )
984 {
985  SIM_TYPE type = m_exporter->GetSimType();
986 
987  if( SIM_PANEL_BASE::IsPlottable( type ) )
988  {
989  SIM_PLOT_PANEL* prevPlot = CurrentPlot();
990  SIM_PLOT_PANEL* newPlot =
991  dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( m_exporter->GetUsedSimCommand() ) );
992 
993  // If the previous plot had the same type, copy the simulation command
994  if( prevPlot )
995  m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand;
996  }
997 }
998 
999 
1000 void SIM_PLOT_FRAME::menuOpenWorkbook( wxCommandEvent& event )
1001 {
1002  wxFileDialog openDlg( this, _( "Open simulation workbook" ), m_savedWorkbooksPath, "",
1003  WorkbookFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1004 
1005  if( openDlg.ShowModal() == wxID_CANCEL )
1006  return;
1007 
1008  m_savedWorkbooksPath = openDlg.GetDirectory();
1009 
1010  if( !loadWorkbook( openDlg.GetPath() ) )
1011  DisplayError( this, _( "There was an error while opening the workbook file" ) );
1012 }
1013 
1014 
1015 void SIM_PLOT_FRAME::menuSaveWorkbook( wxCommandEvent& event )
1016 {
1017  if( !CurrentPlot() )
1018  return;
1019 
1020  wxFileDialog saveDlg( this, _( "Save Simulation Workbook" ), m_savedWorkbooksPath, "",
1021  WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1022 
1023  if( saveDlg.ShowModal() == wxID_CANCEL )
1024  return;
1025 
1026  m_savedWorkbooksPath = saveDlg.GetDirectory();
1027 
1028  if( !saveWorkbook( saveDlg.GetPath() ) )
1029  DisplayError( this, _( "There was an error while saving the workbook file" ) );
1030 }
1031 
1032 
1033 void SIM_PLOT_FRAME::menuSaveImage( wxCommandEvent& event )
1034 {
1035  if( !CurrentPlot() )
1036  return;
1037 
1038  wxFileDialog saveDlg( this, _( "Save Plot as Image" ), "", "",
1039  PngFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1040 
1041  if( saveDlg.ShowModal() == wxID_CANCEL )
1042  return;
1043 
1044  CurrentPlot()->GetPlotWin()->SaveScreenshot( saveDlg.GetPath(), wxBITMAP_TYPE_PNG );
1045 }
1046 
1047 
1048 void SIM_PLOT_FRAME::menuSaveCsv( wxCommandEvent& event )
1049 {
1050  if( !CurrentPlot() )
1051  return;
1052 
1053  const wxChar SEPARATOR = ';';
1054 
1055  wxFileDialog saveDlg( this, _( "Save Plot Data" ), "", "",
1056  CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1057 
1058  if( saveDlg.ShowModal() == wxID_CANCEL )
1059  return;
1060 
1061  wxFFile out( saveDlg.GetPath(), "wb" );
1062  bool timeWritten = false;
1063 
1064  for( const auto& t : CurrentPlot()->GetTraces() )
1065  {
1066  const TRACE* trace = t.second;
1067 
1068  if( !timeWritten )
1069  {
1070  out.Write( wxString::Format( "Time%c", SEPARATOR ) );
1071 
1072  for( double v : trace->GetDataX() )
1073  out.Write( wxString::Format( "%g%c", v, SEPARATOR ) );
1074 
1075  out.Write( "\r\n" );
1076  timeWritten = true;
1077  }
1078 
1079  out.Write( wxString::Format( "%s%c", t.first, SEPARATOR ) );
1080 
1081  for( double v : trace->GetDataY() )
1082  out.Write( wxString::Format( "%g%c", v, SEPARATOR ) );
1083 
1084  out.Write( "\r\n" );
1085  }
1086 
1087  out.Close();
1088 }
1089 
1090 
1091 void SIM_PLOT_FRAME::menuZoomIn( wxCommandEvent& event )
1092 {
1093  if( CurrentPlot() )
1094  CurrentPlot()->GetPlotWin()->ZoomIn();
1095 }
1096 
1097 
1098 void SIM_PLOT_FRAME::menuZoomOut( wxCommandEvent& event )
1099 {
1100  if( CurrentPlot() )
1101  CurrentPlot()->GetPlotWin()->ZoomOut();
1102 }
1103 
1104 
1105 void SIM_PLOT_FRAME::menuZoomFit( wxCommandEvent& event )
1106 {
1107  if( CurrentPlot() )
1108  CurrentPlot()->GetPlotWin()->Fit();
1109 }
1110 
1111 
1112 void SIM_PLOT_FRAME::menuShowGrid( wxCommandEvent& event )
1113 {
1114  SIM_PLOT_PANEL* plot = CurrentPlot();
1115 
1116  if( plot )
1117  plot->ShowGrid( !plot->IsGridShown() );
1118 }
1119 
1120 
1121 void SIM_PLOT_FRAME::menuShowGridUpdate( wxUpdateUIEvent& event )
1122 {
1123  SIM_PLOT_PANEL* plot = CurrentPlot();
1124 
1125  event.Check( plot ? plot->IsGridShown() : false );
1126 }
1127 
1128 
1129 void SIM_PLOT_FRAME::menuShowLegend( wxCommandEvent& event )
1130 {
1131  SIM_PLOT_PANEL* plot = CurrentPlot();
1132 
1133  if( plot )
1134  plot->ShowLegend( !plot->IsLegendShown() );
1135 }
1136 
1137 
1138 void SIM_PLOT_FRAME::menuShowLegendUpdate( wxUpdateUIEvent& event )
1139 {
1140  SIM_PLOT_PANEL* plot = CurrentPlot();
1141  event.Check( plot ? plot->IsLegendShown() : false );
1142 }
1143 
1144 
1145 void SIM_PLOT_FRAME::menuShowDotted( wxCommandEvent& event )
1146 {
1147  SIM_PLOT_PANEL* plot = CurrentPlot();
1148 
1149  if( plot )
1150  plot->SetDottedCurrentPhase( !plot->GetDottedCurrentPhase() );
1151 }
1152 
1153 
1154 void SIM_PLOT_FRAME::menuShowDottedUpdate( wxUpdateUIEvent& event )
1155 {
1156  SIM_PLOT_PANEL* plot = CurrentPlot();
1157 
1158  event.Check( plot ? plot->GetDottedCurrentPhase() : false );
1159 }
1160 
1161 
1162 void SIM_PLOT_FRAME::menuWhiteBackground( wxCommandEvent& event )
1163 {
1165 
1166  // Rebuild the color list to plot traces
1168 
1169  // Now send changes to all SIM_PLOT_PANEL
1170  for( size_t page = 0; page < m_plotNotebook->GetPageCount(); page++ )
1171  {
1172  wxWindow* curPage = m_plotNotebook->GetPage( page );
1173 
1174  if( curPage == m_welcomePanel )
1175  continue;
1176 
1177  // ensure it is truely a plot panel and not the (zero plots) placeholder
1178  // which is only SIM_PLOT_PANEL_BASE
1179  SIM_PLOT_PANEL* panel = dynamic_cast<SIM_PLOT_PANEL*>( curPage );
1180 
1181  if( panel != nullptr )
1182  {
1183  panel->UpdatePlotColors();
1184  }
1185  }
1186 }
1187 
1188 
1189 void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event )
1190 {
1191  int idx = event.GetSelection();
1192 
1193  if( idx == wxNOT_FOUND )
1194  return;
1195 
1196  SIM_PANEL_BASE* plotPanel =
1197  dynamic_cast<SIM_PANEL_BASE*>( m_plotNotebook->GetPage( idx ) );
1198 
1199  m_plots.erase( plotPanel );
1200  updateSignalList();
1201  wxCommandEvent dummy;
1202  onCursorUpdate( dummy );
1203 }
1204 
1205 
1206 void SIM_PLOT_FRAME::onPlotChanged( wxAuiNotebookEvent& event )
1207 {
1208  updateSignalList();
1209  wxCommandEvent dummy;
1210  onCursorUpdate( dummy );
1211 }
1212 
1213 
1214 void SIM_PLOT_FRAME::onSignalDblClick( wxMouseEvent& event )
1215 {
1216  // Remove signal from the plot panel when double clicked
1217  long idx = m_signals->GetFocusedItem();
1218 
1219  if( idx != wxNOT_FOUND )
1220  removePlot( m_signals->GetItemText( idx, 0 ) );
1221 }
1222 
1223 
1224 void SIM_PLOT_FRAME::onSignalRClick( wxListEvent& event )
1225 {
1226  int idx = event.GetIndex();
1227 
1228  if( idx != wxNOT_FOUND )
1229  m_signals->Select( idx );
1230 
1231  idx = m_signals->GetFirstSelected();
1232 
1233  if( idx != wxNOT_FOUND )
1234  {
1235  const wxString& netName = m_signals->GetItemText( idx, 0 );
1236  SIGNAL_CONTEXT_MENU ctxMenu( netName, this );
1237  m_signals->PopupMenu( &ctxMenu );
1238  }
1239 }
1240 
1241 
1242 void SIM_PLOT_FRAME::onSimulate( wxCommandEvent& event )
1243 {
1244  if( IsSimulationRunning() )
1245  StopSimulation();
1246  else
1247  StartSimulation();
1248 }
1249 
1250 
1251 void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event )
1252 {
1253  SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
1254 
1255  if( !m_settingsDlg )
1256  m_settingsDlg = new DIALOG_SIM_SETTINGS( this );
1257 
1258  // Initial processing is required to e.g. display a list of power sources
1260 
1261  if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) )
1262  {
1263  DisplayError( this, _( "There were errors during netlist export, aborted." ) );
1264  return;
1265  }
1266 
1267  if( plotPanelWindow != m_welcomePanel )
1268  m_settingsDlg->SetSimCommand( m_plots[plotPanelWindow].m_simCommand );
1269 
1270  if( m_settingsDlg->ShowModal() == wxID_OK )
1271  {
1272  wxString oldCommand = m_plots[plotPanelWindow].m_simCommand;
1273  wxString newCommand = m_settingsDlg->GetSimCommand();
1274  SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand );
1275 
1276  // If it is a new simulation type, open a new plot
1277  // For the DC sim, check if sweep source type has changed (char 4 will contain 'v', 'i', 'r' or 't'
1278  if( !plotPanelWindow
1279  || ( plotPanelWindow && plotPanelWindow->GetType() != newSimType )
1280  || ( newSimType == ST_DC
1281  && oldCommand.Lower().GetChar( 4 ) != newCommand.Lower().GetChar( 4 ) ) )
1282  {
1283  plotPanelWindow = NewPlotPanel( newCommand );
1284  }
1285 
1286  m_plots[plotPanelWindow].m_simCommand = newCommand;
1287  }
1288 }
1289 
1290 
1291 void SIM_PLOT_FRAME::onAddSignal( wxCommandEvent& event )
1292 {
1293  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1294 
1295  if( !plotPanel || !m_exporter || plotPanel->GetType() != m_exporter->GetSimType() )
1296  {
1297  DisplayInfoMessage( this, _( "You need to run plot-providing simulation first." ) );
1298  return;
1299  }
1300 
1301  DIALOG_SIGNAL_LIST dialog( this, m_exporter.get() );
1302  dialog.ShowModal();
1303 }
1304 
1305 
1306 void SIM_PLOT_FRAME::onProbe( wxCommandEvent& event )
1307 {
1308  if( m_schematicFrame == NULL )
1309  return;
1310 
1312  m_schematicFrame->Raise();
1313 }
1314 
1315 
1316 void SIM_PLOT_FRAME::onTune( wxCommandEvent& event )
1317 {
1318  if( m_schematicFrame == NULL )
1319  return;
1320 
1322  m_schematicFrame->Raise();
1323 }
1324 
1325 void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event )
1326 {
1327  class NETLIST_VIEW_DIALOG : public DIALOG_SHIM
1328  {
1329  public:
1330  enum
1331  {
1332  MARGIN_LINE_NUMBERS
1333  };
1334 
1335  void onClose( wxCloseEvent& evt )
1336  {
1337  EndModal( GetReturnCode() );
1338  }
1339 
1340  NETLIST_VIEW_DIALOG( wxWindow* parent, wxString source) :
1341  DIALOG_SHIM( parent, wxID_ANY, "SPICE Netlist",
1342  wxDefaultPosition, wxDefaultSize,
1343  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
1344  {
1345  wxStyledTextCtrl* text = new wxStyledTextCtrl( this, wxID_ANY );
1346  text->SetMinSize( wxSize( 600, 400 ) );
1347 
1348  text->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 );
1349  text->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) );
1350  text->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) );
1351  text->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER );
1352 
1353  wxFont font = wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_TELETYPE,
1354  wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxEmptyString );
1355  text->StyleSetFont( wxSTC_STYLE_DEFAULT, font );
1356 
1357  text->SetWrapMode( wxSTC_WRAP_WORD );
1358 
1359  text->SetText( source );
1360 
1361  text->StyleClearAll();
1362  text->SetLexer( wxSTC_LEX_SPICE );
1363 
1364  wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
1365  sizer->Add( text, 1, wxEXPAND );
1366  SetSizer( sizer );
1367 
1368  Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), NULL,
1369  this );
1370 
1371  finishDialogSettings();
1372  }
1373  };
1374 
1375  if( m_schematicFrame == NULL || m_simulator == NULL )
1376  return;
1377 
1378  NETLIST_VIEW_DIALOG dlg( this, m_simulator->GetNetlist() );
1379  dlg.ShowModal();
1380 }
1381 
1382 
1384 {
1385  SaveSettings( config() );
1386 
1387  if( IsSimulationRunning() )
1388  m_simulator->Stop();
1389 
1390  // Cancel a running simProbe or simTune tool
1392 
1393  Destroy();
1394 }
1395 
1396 
1397 void SIM_PLOT_FRAME::onCursorUpdate( wxCommandEvent& event )
1398 {
1399  wxSize size = m_cursors->GetClientSize();
1400  SIM_PLOT_PANEL* plotPanel = CurrentPlot();
1401  m_cursors->ClearAll();
1402 
1403  if( !plotPanel )
1404  return;
1405 
1407  m_cursors->SetImageList(m_signalsIconColorList, wxIMAGE_LIST_SMALL);
1408 
1409  // Fill the signals listctrl
1410  m_cursors->AppendColumn( _( "Signal" ), wxLIST_FORMAT_LEFT, size.x / 2 );
1411  const long X_COL = m_cursors->AppendColumn( plotPanel->GetLabelX(), wxLIST_FORMAT_LEFT, size.x / 4 );
1412 
1413  wxString labelY1 = plotPanel->GetLabelY1();
1414  wxString labelY2 = plotPanel->GetLabelY2();
1415  wxString labelY;
1416 
1417  if( !labelY2.IsEmpty() )
1418  labelY = labelY1 + " / " + labelY2;
1419  else
1420  labelY = labelY1;
1421 
1422  const long Y_COL = m_cursors->AppendColumn( labelY, wxLIST_FORMAT_LEFT, size.x / 4 );
1423 
1424  // Update cursor values
1425  int itemidx = 0;
1426 
1427  for( const auto& trace : plotPanel->GetTraces() )
1428  {
1429  if( CURSOR* cursor = trace.second->GetCursor() )
1430  {
1431  // Find the right icon color in list.
1432  // It is the icon used in m_signals list for the same trace
1433  long iconColor = m_signals->FindItem( -1, trace.first );
1434 
1435  const wxRealPoint coords = cursor->GetCoords();
1436  long idx = m_cursors->InsertItem( itemidx++, trace.first, iconColor );
1437  m_cursors->SetItem( idx, X_COL, SPICE_VALUE( coords.x ).ToSpiceString() );
1438  m_cursors->SetItem( idx, Y_COL, SPICE_VALUE( coords.y ).ToSpiceString() );
1439  }
1440  }
1441 }
1442 
1443 
1444 void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent )
1445 {
1446  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_stop_xpm ) );
1447  SetCursor( wxCURSOR_ARROWWAIT );
1448 }
1449 
1450 
1451 void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
1452 {
1453  m_toolBar->SetToolNormalBitmap( ID_SIM_RUN, KiBitmap( sim_run_xpm ) );
1454  SetCursor( wxCURSOR_ARROW );
1455 
1456  SIM_TYPE simType = m_exporter->GetSimType();
1457 
1458  if( simType == ST_UNKNOWN )
1459  return;
1460 
1461  SIM_PANEL_BASE* plotPanelWindow = currentPlotWindow();
1462 
1463  if( !plotPanelWindow || plotPanelWindow->GetType() != simType )
1464  plotPanelWindow = NewPlotPanel( m_exporter->GetUsedSimCommand() );
1465 
1466  if( IsSimulationRunning() )
1467  return;
1468 
1469  // If there are any signals plotted, update them
1470  if( SIM_PANEL_BASE::IsPlottable( simType ) )
1471  {
1472  TRACE_MAP& traceMap = m_plots[plotPanelWindow].m_traces;
1473  SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( plotPanelWindow );
1474 
1475  wxCHECK_RET( plotPanel, "not a SIM_PLOT_PANEL" );
1476 
1477  for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */)
1478  {
1479  if( !updatePlot( it->second, plotPanel ) )
1480  {
1481  removePlot( it->first, false );
1482  it = traceMap.erase( it ); // remove a plot that does not exist anymore
1483  }
1484  else
1485  {
1486  ++it;
1487  }
1488  }
1489 
1490  updateSignalList();
1491  plotPanel->GetPlotWin()->UpdateAll();
1492  plotPanel->ResetScales();
1493  }
1494  else if( simType == ST_OP )
1495  {
1496  m_simConsole->AppendText( _( "\n\nSimulation results:\n\n" ) );
1497  m_simConsole->SetInsertionPointEnd();
1498 
1499  for( const auto& vec : m_simulator->AllPlots() )
1500  {
1501  double val = m_simulator->GetRealPlot( vec, 1 ).at( 0 );
1502 
1503  wxString outLine, signal;
1504  SIM_PLOT_TYPE type = m_exporter->VectorToSignal( vec, signal );
1505 
1506  const size_t tab = 25; //characters
1507  size_t padding = ( signal.length() < tab ) ? ( tab - signal.length() ) : 1;
1508 
1509  outLine.Printf( wxT( "%s%s" ), ( signal + wxT( ":" ) ).Pad( padding, wxUniChar( ' ' ) ),
1510  SPICE_VALUE( val ).ToSpiceString() );
1511 
1512  outLine.Append( type == SPT_CURRENT ? "A\n" : "V\n" );
1513 
1514  m_simConsole->AppendText( outLine );
1515  m_simConsole->SetInsertionPointEnd();
1516 
1517  // @todo display calculated values on the schematic
1518  }
1519  }
1520 }
1521 
1522 
1523 void SIM_PLOT_FRAME::onSimUpdate( wxCommandEvent& aEvent )
1524 {
1525  if( IsSimulationRunning() )
1526  StopSimulation();
1527 
1528  if( CurrentPlot() != m_lastSimPlot )
1529  {
1530  // We need to rerun simulation, as the simulator currently stores
1531  // results for another plot
1532  StartSimulation();
1533  }
1534  else
1535  {
1536  // Incremental update
1537  m_simConsole->Clear();
1538  // Do not export netlist, it is already stored in the simulator
1539  applyTuners();
1540  m_simulator->Run();
1541  }
1542 }
1543 
1544 
1545 void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent )
1546 {
1547  m_simConsole->AppendText( aEvent.GetString() + "\n" );
1548  m_simConsole->SetInsertionPointEnd();
1549 }
1550 
1551 
1553  SIM_PLOT_FRAME* aPlotFrame ) :
1554  m_signal( aSignal ),
1555  m_plotFrame( aPlotFrame )
1556 {
1558 
1559  AddMenuItem( this, HIDE_SIGNAL, _( "Hide Signal" ),
1560  _( "Erase the signal from plot screen" ),
1561  KiBitmap( trash_xpm ) );
1562 
1563  TRACE* trace = plot->GetTrace( m_signal );
1564 
1565  if( trace->HasCursor() )
1566  AddMenuItem( this, HIDE_CURSOR, _( "Hide Cursor" ), KiBitmap( pcb_target_xpm ) );
1567  else
1568  AddMenuItem( this, SHOW_CURSOR, _( "Show Cursor" ), KiBitmap( pcb_target_xpm ) );
1569 
1570  Connect( wxEVT_COMMAND_MENU_SELECTED, wxMenuEventHandler( SIGNAL_CONTEXT_MENU::onMenuEvent ), NULL, this );
1571 }
1572 
1573 
1575 {
1576  SIM_PLOT_PANEL* plot = m_plotFrame->CurrentPlot();
1577 
1578  switch( aEvent.GetId() )
1579  {
1580  case HIDE_SIGNAL:
1581  m_plotFrame->removePlot( m_signal );
1582  break;
1583 
1584  case SHOW_CURSOR:
1585  plot->EnableCursor( m_signal, true );
1586  break;
1587 
1588  case HIDE_CURSOR:
1589  plot->EnableCursor( m_signal, false );
1590  break;
1591  }
1592 }
1593 
1594 wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
1595 wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
1596 
1597 wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
1598 wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );
Field Reference of part, i.e. "IC21".
const BITMAP_OPAQUE simulator_xpm[1]
Definition: simulator.cpp:71
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:252
void onAddSignal(wxCommandEvent &event)
#define ID_MENU_PROBE_SIGNALS
const BITMAP_OPAQUE trash_xpm[1]
Definition: trash.cpp:31
const BITMAP_OPAQUE swap_layer_xpm[1]
Definition: swap_layer.cpp:69
#define ID_SAVE_AS_IMAGE
void ZoomOut(const wxPoint &centerPoint=wxDefaultPosition)
Zoom out current view and refresh display.
Definition: mathplot.cpp:2276
bool IsLegendShown() const
bool saveWorkbook(const wxString &aPath)
Save plot settings to a file.
wxString ToSpiceString() const
Return string value in Spice format (e.g.
void SetKiway(wxWindow *aDest, KIWAY *aKiway)
It is only used for debugging, since "this" is not a wxWindow*.
wxToolBarToolBase * m_toolProbe
void setIconsForMenuItems()
Give icons to menuitems of the main menubar.
void onSimStarted(wxCommandEvent &aEvent)
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:56
void RemoveTuner(TUNER_SLIDER *aTuner, bool aErase=true)
Remove an existing tuner.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
bool GetPlotBgOpt() const
wxImageList * m_signalsIconColorList
A string to store the path of saved workbooks during a session.
SIM_PLOT_TYPE operator|(SIM_PLOT_TYPE aFirst, SIM_PLOT_TYPE aSecond)
void onPlotChanged(wxAuiNotebookEvent &event) override
PNG memory record (file in memory).
Definition: bitmap_def.h:29
SIM_PLOT_FRAME * m_parent
const BITMAP_OPAQUE add_dashed_line_xpm[1]
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Returns a mandatory field in this symbol.
Definition: sch_symbol.cpp:688
void EnableMouseWheelPan(bool enabled)
Enable/disable trackpad friendly panning (2-axis scroll wheel)
Definition: mathplot.h:1217
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
void menuWhiteBackground(wxCommandEvent &event) override
void onSignalDblClick(wxMouseEvent &event) override
bool SetSimCommand(const wxString &aCommand)
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Save common frame parameters to a configuration data file.
mpWindow * GetPlotWin() const
This file is part of the common library.
wxSplitterWindow * m_splitterSignals
const wxString & GetTitle() const
bool HasCursor() const
SEVERITY
Definition: ui_common.h:83
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
Definition: bitmap.cpp:221
SIM_PANEL_BASE * NewPlotPanel(wxString aSimCommand)
Create a new plot panel for a given simulation type and adds it to the main notebook.
void ShowLegend(bool aEnable)
int m_splitterPlotAndConsoleSashPosition
void menuZoomFit(wxCommandEvent &event) override
Structure to represent a schematic component in the Spice simulation.
const std::vector< double > & GetDataX() const
Map of plot panels and associated data.
std::list< TUNER_SLIDER * > m_tuners
static TOOL_ACTION cancelInteractive
Definition: actions.h:65
void doCloseWindow() override
SIM_PLOT_TYPE GetType() const
wxString m_title
< Title displayed in the signal list/plot legend
Class SIM_PLOT_FRAME_BASE.
const BITMAP_OPAQUE text_xpm[1]
Definition: text.cpp:17
bool IsGridShown() const
bool GetDottedCurrentPhase() const
Returns true if the trace has cursor shown.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:141
WINDOW_SETTINGS * GetWindowSettings(APP_SETTINGS_BASE *aCfg) override
Return a pointer to the window settings for this frame.
void menuNewPlot(wxCommandEvent &aEvent) override
void applyTuners()
Apply component values specified using tuner sliders to the current netlist.
const BITMAP_OPAQUE zoom_fit_in_page_xpm[1]
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
void AddVoltagePlot(const wxString &aNetName)
Add a voltage plot for a given net name.
void AddCurrentPlot(const wxString &aDeviceName, const wxString &aParam)
Add a current plot for a particular device.
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:345
#define ID_MENU_WHITE_BG
Trace descriptor class
Schematic editor (Eeschema) main window.
wxString CsvFileWildcard()
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
void onSimUpdate(wxCommandEvent &aEvent)
void menuOpenWorkbook(wxCommandEvent &event) override
const BITMAP_OPAQUE netlist_xpm[1]
void SetDottedCurrentPhase(bool aEnable)
void menuShowGrid(wxCommandEvent &event) override
void menuZoomIn(wxCommandEvent &event) override
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
std::map< SIM_PANEL_BASE *, PLOT_INFO > m_plots
List of currently displayed tuners.
void menuZoomOut(wxCommandEvent &event) override
SIM_THREAD_REPORTER * m_reporter
void OnSimStateChange(SPICE_SIMULATOR *aObject, SIM_STATE aNewState) override
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
std::shared_ptr< SPICE_SIMULATOR > m_simulator
bool updatePlot(const TRACE_DESC &aDescriptor, SIM_PLOT_PANEL *aPanel)
Update plot in a particular SIM_PLOT_PANEL.
TRACE * GetTrace(const wxString &aName) const
SIM_PANEL_BASE * m_welcomePanel
#define ID_SAVE_AS_CSV
virtual const wxString GetProjectPath() const
Return the full path of the project.
wxString GetLabelY1() const
wxSplitterWindow * m_splitterTuneValues
static bool IsPlottable(SIM_TYPE aSimType)
static SIM_TYPE CommandToSimType(const wxString &aCmd)
Return simulation type basing on a simulation command directive.
SIM_PLOT_TYPE GetXAxisType(SIM_TYPE aType) const
Return X axis for a given simulation type.
void menuSaveCsv(wxCommandEvent &event) override
void onPlotClose(wxAuiNotebookEvent &event) override
void StartSimulation(const wxString &aSimCommand=wxEmptyString)
const wxString & GetParam() const
wxAuiNotebook * m_plotNotebook
SCH_EDIT_FRAME * m_schematicFrame
bool DeleteTrace(const wxString &aName)
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2750
void EnableCursor(const wxString &aName, bool aEnable)
Resets scale ranges to fit the current traces.
SIM_STATE
const wxString & GetSimCommand() const
void setSubWindowsSashSize()
const BITMAP_OPAQUE zoom_out_xpm[1]
Definition: zoom_out.cpp:36
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
Stores the common settings that are saved and loaded for each window / frame.
Definition: app_settings.h:81
wxString ToString() const
Return string value as when converting double to string (e.g.
const BITMAP_OPAQUE sim_add_signal_xpm[1]
SIM_PLOT_PANEL * m_lastSimPlot
imagelists used to add a small colored icon to signal names and cursors name, the same color as the c...
wxString GetLabelX() const
void AddBitmapToMenuItem(wxMenuItem *aMenu, const wxBitmap &aImage)
Add a bitmap to a menuitem.
Definition: bitmap.cpp:207
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:99
#define NULL
#define ID_MENU_SHOW_NETLIST
wxString GetLabelY2() const
static TOOL_ACTION simTune
Definition: ee_actions.h:210
static void FillDefaultColorList(bool aWhiteBg)
Fills m_colorList by a default set of colors.
< Helper class to handle Spice way of expressing values (e.g. 10.5 Meg) Helper class to recognize Spi...
Definition: spice_value.h:34
wxSplitterWindow * m_splitterLeftRight
void ZoomIn(const wxPoint &centerPoint=wxDefaultPosition)
Zoom into current view and refresh display.
Definition: mathplot.cpp:2219
wxMenuItem * m_runSimulation
#define ID_MENU_TUNE_SIGNALS
void onSimReport(wxCommandEvent &aEvent)
const BITMAP_OPAQUE exit_xpm[1]
Definition: exit.cpp:50
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
SIM_TYPE
< Possible simulation types
Definition: sim_types.h:31
void onSimFinished(wxCommandEvent &aEvent)
Interface to receive simulation updates from SPICE_SIMULATOR class.
const BITMAP_OPAQUE grid_xpm[1]
Definition: grid.cpp:38
const BITMAP_OPAQUE export_xpm[1]
Definition: export.cpp:40
bool AddTrace(const wxString &aName, int aPoints, const double *aX, const double *aY, SIM_PLOT_TYPE aFlags)
SCHEMATIC & Schematic() const
Definition of file extensions used in Kicad.
void onShowNetlist(wxCommandEvent &event)
SIM_THREAD_REPORTER(SIM_PLOT_FRAME *aParent)
const BITMAP_OPAQUE directory_open_xpm[1]
void menuShowLegendUpdate(wxUpdateUIEvent &event) override
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:260
wxToolBarToolBase * m_toolAddSignals
Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder.
int m_splitterTuneValuesSashPosition
void SaveSettings(APP_SETTINGS_BASE *aCfg) override
Save common frame parameters to a configuration data file.
static std::shared_ptr< SPICE_SIMULATOR > CreateInstance(const std::string &aName)
Initialize the simulator.
TRACE_DESC(const NETLIST_EXPORTER_PSPICE_SIM &aExporter, const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam)
Modifies an existing TRACE_DESC simulation type.
SIM_PANEL_BASE * currentPlotWindow() const
Return the currently opened plot panel (or NULL if there is none).
const std::string & GetString()
Definition: richio.h:435
void onSignalRClick(wxListEvent &event) override
void ResetScales()
Update trace line style.
bool IsSimulationRunning()
#define ID_MENU_SHOW_LEGEND
void menuShowLegend(wxCommandEvent &event) override
void ShowGrid(bool aEnable)
void onTune(wxCommandEvent &event)
wxToolBarToolBase * m_toolSimulate
void menuSaveImage(wxCommandEvent &event) override
#define ID_MENU_SET_SIMUL
wxString PngFileWildcard()
Implementing SIM_PLOT_FRAME_BASE.
bool loadWorkbook(const wxString &aPath)
Load plot settings from a file.
void SetNetlistExporter(NETLIST_EXPORTER_PSPICE_SIM *aExporter)
static wxString m_savedWorkbooksPath
Info panel.
void onCursorUpdate(wxCommandEvent &aEvent)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
SIM_TYPE GetType() const
DIALOG_SIM_SETTINGS * m_settingsDlg
void Fit() override
Set view to fit global bounding box of all plot layers and refresh display.
Definition: mathplot.cpp:2014
const wxString & GetName() const
const wxString What() const
Definition: ki_exception.h:57
const BITMAP_OPAQUE sim_probe_xpm[1]
Definition: sim_probe.cpp:35
void UpdatePlotColors()
Update plot colors
#define ID_MENU_ADD_SIGNAL
const BITMAP_OPAQUE sim_stop_xpm[1]
Definition: sim_stop.cpp:23
std::unique_ptr< NETLIST_EXPORTER_PSPICE_SIM > m_exporter
void updateNetlistExporter()
Reload the current schematic for the netlist exporter.
see class PGM_BASE
void onSimulate(wxCommandEvent &event)
const BITMAP_OPAQUE sim_tune_xpm[1]
Definition: sim_tune.cpp:27
const char * name
Definition: DXF_plotter.cpp:59
SIGNAL_CONTEXT_MENU(const wxString &aSignal, SIM_PLOT_FRAME *aPlotFrame)
void onProbe(wxCommandEvent &event)
#define ID_MENU_SHOW_GRID
#define _(s)
Definition: 3d_actions.cpp:33
void updateSignalList()
Update the list of currently plotted signals.
#define ID_MENU_DOTTED
unsigned int m_plotNumber
static wxString GetSpiceField(SPICE_FIELD aField, SCH_COMPONENT *aComponent, unsigned aCtl)
Retrieve either the requested field value or the default value.
Schematic symbol object.
Definition: sch_symbol.h:78
int m_splitterSignalsSashPosition
const BITMAP_OPAQUE sim_run_xpm[1]
Definition: sim_run.cpp:27
SIM_PLOT_PANEL * CurrentPlot() const
Return the currently opened plot panel (or NULL if there is none).
std::map< wxString, TRACE_DESC > TRACE_MAP
void menuShowDotted(wxCommandEvent &event) override
wxDEFINE_EVENT(EVT_SIM_UPDATE, wxCommandEvent)
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
const BITMAP_OPAQUE save_xpm[1]
Definition: save.cpp:26
const std::vector< double > & GetDataY() const
int m_splitterLeftRightSashPosition
void menuShowDottedUpdate(wxUpdateUIEvent &event) override
static TOOL_ACTION simProbe
Definition: ee_actions.h:209
const NETLIST_EXPORTER_PSPICE_SIM * GetExporter() const
Return the netlist exporter object used for simulations.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
void removePlot(const wxString &aPlotName, bool aErase=true)
Remove a plot with a specific title.
Custom widget to handle quick component values modification and simulation on the fly.
Definition: tuner_slider.h:42
void AddTuner(SCH_COMPONENT *aComponent)
Add a tuner for a component.
wxString WorkbookFileWildcard()
bool SaveScreenshot(const wxString &filename, wxBitmapType type=wxBITMAP_TYPE_BMP, wxSize imageSize=wxDefaultSize, bool fit=false)
Draw the window on a wxBitmap, then save it to a file.
Definition: mathplot.cpp:2980
const std::map< wxString, TRACE * > & GetTraces() const
wxSplitterWindow * m_splitterPlotAndConsole
void menuShowGridUpdate(wxUpdateUIEvent &event) override
bool HasMessage() const override
Returns true if the reporter client is non-empty.
const BITMAP_OPAQUE config_xpm[1]
Definition: config.cpp:38
void menuSaveWorkbook(wxCommandEvent &event) override
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:280
Implement an OUTPUTFORMATTER to a memory buffer.
Definition: richio.h:411
SIM_PLOT_TYPE
Definition: sim_types.h:46
#define ID_MENU_RUN_SIM
Class is responsible for providing colors for traces on simulation plot.
Panel that was used as the most recent one for simulations.
void addPlot(const wxString &aName, SIM_PLOT_TYPE aType, const wxString &aParam)
Add a new plot to the current panel.
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
void onSettings(wxCommandEvent &event)
void LoadSettings(APP_SETTINGS_BASE *aCfg) override
Load common frame parameters from a configuration file.
wxToolBarToolBase * m_toolTune
void onMenuEvent(wxMenuEvent &aEvent)
const BITMAP_OPAQUE pcb_target_xpm[1]
bool TraceShown(const wxString &aName) const
Cursor attached to a trace to follow its values:
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
Special netlist exporter flavor that allows one to override simulation commands.
void updateTuners()
Filter out tuners for components that do not exist anymore.
Hold a translatable error message and may be used when throwing exceptions containing a translated er...
Definition: ki_exception.h:44
wxToolBarToolBase * m_toolSettings
SIM_PLOT_FRAME(KIWAY *aKiway, wxWindow *aParent)
Constructor.
const BITMAP_OPAQUE zoom_in_xpm[1]
Definition: zoom_in.cpp:37