KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_plot_panel.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-2023 CERN
5 * Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 * @author Maciej Suminski <[email protected]>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 3
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you may find one here:
22 * https://www.gnu.org/licenses/gpl-3.0.html
23 * or you may search the http://www.gnu.org website for the version 3 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28#include "sim_plot_colors.h"
29#include "sim_plot_panel.h"
30#include "simulator_frame.h"
31#include "core/kicad_algo.h"
32
33#include <algorithm>
34#include <limits>
35
36
37static wxString formatFloat( double x, int nDigits )
38{
39 wxString rv, fmt;
40
41 if( nDigits )
42 fmt.Printf( "%%.0%df", nDigits );
43 else
44 fmt = wxT( "%.0f" );
45
46 rv.Printf( fmt, x );
47
48 return rv;
49}
50
51
52static void getSISuffix( double x, const wxString& unit, int& power, wxString& suffix )
53{
54 const int n_powers = 11;
55
56 const struct
57 {
58 int exponent;
59 char suffix;
60 } powers[] =
61 {
62 { -18, 'a' },
63 { -15, 'f' },
64 { -12, 'p' },
65 { -9, 'n' },
66 { -6, 'u' },
67 { -3, 'm' },
68 { 0, 0 },
69 { 3, 'k' },
70 { 6, 'M' },
71 { 9, 'G' },
72 { 12, 'T' },
73 { 14, 'P' }
74 };
75
76 power = 0;
77 suffix = unit;
78
79 if( x == 0.0 )
80 return;
81
82 for( int i = 0; i < n_powers - 1; i++ )
83 {
84 double r_cur = pow( 10, powers[i].exponent );
85
86 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
87 {
88 power = powers[i].exponent;
89
90 if( powers[i].suffix )
91 suffix = wxString( powers[i].suffix ) + unit;
92 else
93 suffix = unit;
94
95 return;
96 }
97 }
98}
99
100
101static int countDecimalDigits( double x, int maxDigits )
102{
103 if( std::isnan( x ) )
104 {
105 // avoid trying to count the decimals of NaN
106 return 0;
107 }
108
109 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (double) maxDigits ) );
110 int n = 0;
111
112 while( k && ( ( k % 10LL ) == 0LL || ( k % 10LL ) == 9LL ) )
113 {
114 k /= 10LL;
115 }
116
117 n = 0;
118
119 while( k != 0LL )
120 {
121 n++;
122 k /= 10LL;
123 }
124
125 return n;
126}
127
128
129template <typename parent>
130class LIN_SCALE : public parent
131{
132public:
133 LIN_SCALE( const wxString& name, const wxString& unit, int flags ) :
134 parent( name, flags, false ),
135 m_unit( unit )
136 {};
137
138 wxString GetUnits() const { return m_unit; }
139
140private:
141 void formatLabels() override
142 {
143 double maxVis = parent::AbsVisibleMaxValue();
144
145 wxString suffix;
146 int power = 0;
147 int digits = 0;
148 int constexpr DIGITS = 3;
149
150 getSISuffix( maxVis, m_unit, power, suffix );
151
152 double sf = pow( 10.0, power );
153
154 for( mpScaleBase::TickLabel& l : parent::TickLabels() )
155 {
156 int k = countDecimalDigits( l.pos / sf, DIGITS );
157
158 digits = std::max( digits, k );
159 }
160
161 for( mpScaleBase::TickLabel& l : parent::TickLabels() )
162 {
163 l.label = formatFloat( l.pos / sf, digits ) + suffix;
164 l.visible = true;
165 }
166 }
167
168private:
169 const wxString m_unit;
170};
171
172
173template <typename parent>
174class LOG_SCALE : public parent
175{
176public:
177 LOG_SCALE( const wxString& name, const wxString& unit, int flags ) :
178 parent( name, flags, false ),
179 m_unit( unit )
180 {};
181
182 wxString GetUnits() const { return m_unit; }
183
184private:
185 void formatLabels() override
186 {
187 wxString suffix;
188 int power;
189
190 for( mpScaleBase::TickLabel& l : parent::TickLabels() )
191 {
192 getSISuffix( l.pos, m_unit, power, suffix );
193 double sf = pow( 10.0, power );
194 int k = countDecimalDigits( l.pos / sf, 3 );
195
196 l.label = formatFloat( l.pos / sf, k ) + suffix;
197 l.visible = true;
198 }
199 }
200
201private:
202 const wxString m_unit;
203};
204
205
206void CURSOR::SetCoordX( double aValue )
207{
208 wxRealPoint oldCoords = m_coords;
209
210 doSetCoordX( aValue );
211 m_updateRequired = false;
212 m_updateRef = true;
213
214 if( m_window )
215 {
216 wxRealPoint delta = m_coords - oldCoords;
217 mpInfoLayer::Move( wxPoint( m_window->x2p( m_trace->x2s( delta.x ) ),
218 m_window->y2p( m_trace->y2s( delta.y ) ) ) );
219
220 m_window->Refresh();
221 }
222}
223
224
225void CURSOR::doSetCoordX( double aValue )
226{
227 m_coords.x = aValue;
228
229 const std::vector<double>& dataX = m_trace->GetDataX();
230 const std::vector<double>& dataY = m_trace->GetDataY();
231
232 if( dataX.size() <= 1 )
233 return;
234
235 // Find the closest point coordinates
236 auto maxXIt = std::upper_bound( dataX.begin(), dataX.end(), m_coords.x );
237 int maxIdx = maxXIt - dataX.begin();
238 int minIdx = maxIdx - 1;
239
240 // Out of bounds checks
241 if( minIdx < 0 )
242 {
243 minIdx = 0;
244 maxIdx = 1;
245 m_coords.x = dataX[0];
246 }
247 else if( maxIdx >= (int) dataX.size() )
248 {
249 maxIdx = dataX.size() - 1;
250 minIdx = maxIdx - 1;
251 m_coords.x = dataX[maxIdx];
252 }
253
254 const double leftX = dataX[minIdx];
255 const double rightX = dataX[maxIdx];
256 const double leftY = dataY[minIdx];
257 const double rightY = dataY[maxIdx];
258
259 // Linear interpolation
260 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * ( m_coords.x - leftX );
261}
262
263
265{
266 for( const auto& [ id, cursor ] : m_trace->GetCursors() )
267 {
268 if( cursor == this )
269 return wxString::Format( _( "%d" ), id );
270 }
271
272 return wxEmptyString;
273}
274
275
276void CURSOR::Plot( wxDC& aDC, mpWindow& aWindow )
277{
278 if( !m_window )
279 m_window = &aWindow;
280
281 if( !m_visible || m_trace->GetDataX().size() <= 1 )
282 return;
283
284 if( m_updateRequired )
285 {
286 doSetCoordX( m_trace->s2x( aWindow.p2x( m_dim.x ) ) );
287 m_updateRequired = false;
288
289 // Notify the parent window about the changes
290 wxQueueEvent( aWindow.GetParent(), new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
291 }
292 else
293 {
294 m_updateRef = true;
295 }
296
297 if( m_updateRef )
298 {
300 m_updateRef = false;
301 }
302
303 // Line length in horizontal and vertical dimensions
304 const wxPoint cursorPos( aWindow.x2p( m_trace->x2s( m_coords.x ) ),
305 aWindow.y2p( m_trace->y2s( m_coords.y ) ) );
306
307 wxCoord leftPx = m_drawOutsideMargins ? 0 : aWindow.GetMarginLeft();
308 wxCoord rightPx = m_drawOutsideMargins ? aWindow.GetScrX() :
309 aWindow.GetScrX() - aWindow.GetMarginRight();
310 wxCoord topPx = m_drawOutsideMargins ? 0 : aWindow.GetMarginTop();
311 wxCoord bottomPx = m_drawOutsideMargins ? aWindow.GetScrY() :
312 aWindow.GetScrY() - aWindow.GetMarginBottom();
313
314 wxPen pen = GetPen();
315 wxColour fg = GetPen().GetColour();
316
317 pen.SetColour( COLOR4D( m_trace->GetTraceColour() ).Mix( fg, 0.6 ).ToColour() );
318 pen.SetStyle( m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
319 aDC.SetPen( pen );
320
321 if( topPx < cursorPos.y && cursorPos.y < bottomPx )
322 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
323
324 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
325 {
326 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
327
328 wxString id = getID();
329 wxSize size = aDC.GetTextExtent( wxS( "M" ) );
330 wxRect textRect( wxPoint( cursorPos.x + 1 - size.x / 2, topPx - 4 - size.y ), size );
331 wxBrush brush;
332 wxPoint poly[3];
333
334 // Because a "1" looks off-center if it's actually centred.
335 if( id == "1" )
336 textRect.x -= 1;
337
338 // We want an equalateral triangle, so use size.y for both axes.
339 size.y += 3;
340 // Make sure it's an even number so the slopes of the sides will be identical.
341 size.y = ( size.y / 2 ) * 2;
342 poly[0] = { cursorPos.x - 1 - size.y / 2, topPx - size.y };
343 poly[1] = { cursorPos.x + 1 + size.y / 2, topPx - size.y };
344 poly[2] = { cursorPos.x, topPx };
345
346 brush.SetStyle( wxBRUSHSTYLE_SOLID );
347 brush.SetColour( m_trace->GetTraceColour() );
348 aDC.SetBrush( brush );
349 aDC.DrawPolygon( 3, poly );
350
351 aDC.SetTextForeground( fg );
352 aDC.DrawLabel( id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
353 }
354}
355
356
357bool CURSOR::Inside( const wxPoint& aPoint ) const
358{
359 if( !m_window || !m_trace )
360 return false;
361
362 return ( std::abs( (double) aPoint.x -
363 m_window->x2p( m_trace->x2s( m_coords.x ) ) ) <= DRAG_MARGIN )
364 || ( std::abs( (double) aPoint.y -
365 m_window->y2p( m_trace->y2s( m_coords.y ) ) ) <= DRAG_MARGIN );
366}
367
368
370{
371 if( !m_window )
372 return;
373
376}
377
378
379SIM_PLOT_PANEL::SIM_PLOT_PANEL( const wxString& aCommand, int aOptions, wxWindow* parent,
380 wxWindowID id, const wxPoint& pos, const wxSize& size, long style,
381 const wxString& name ) :
382 SIM_PANEL_BASE( aCommand, aOptions, parent, id, pos, size, style, name ),
383 m_axis_x( nullptr ),
384 m_axis_y1( nullptr ),
385 m_axis_y2( nullptr ),
386 m_axis_y3( nullptr ),
387 m_dotted_cp( false )
388{
389 m_sizer = new wxBoxSizer( wxVERTICAL );
390 m_plotWin = new mpWindow( this, wxID_ANY, pos, size, style );
391
392 m_plotWin->LimitView( true );
393 m_plotWin->SetMargins( 35, 70, 35, 70 );
394
396
397 updateAxes();
398
399 // a mpInfoLegend displays le name of traces on the left top panel corner:
400 m_legend = new mpInfoLegend( wxRect( 0, 40, 200, 40 ), wxTRANSPARENT_BRUSH );
401 m_legend->SetVisible( false );
403
406
407 m_sizer->Add( m_plotWin, 1, wxALL | wxEXPAND, 1 );
408 SetSizer( m_sizer );
409}
410
411
413{
414 // ~mpWindow destroys all the added layers, so there is no need to destroy m_traces contents
415}
416
417
419{
420 LOG_SCALE<mpScaleXLog>* logScale = dynamic_cast<LOG_SCALE<mpScaleXLog>*>( m_axis_x );
421 LIN_SCALE<mpScaleX>* linScale = dynamic_cast<LIN_SCALE<mpScaleX>*>( m_axis_x );
422
423 if( logScale )
424 return logScale->GetUnits();
425 else if( linScale )
426 return linScale->GetUnits();
427 else
428 return wxEmptyString;
429}
430
431
433{
434 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y1 );
435
436 if( linScale )
437 return linScale->GetUnits();
438 else
439 return wxEmptyString;
440}
441
442
444{
445 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y2 );
446
447 if( linScale )
448 return linScale->GetUnits();
449 else
450 return wxEmptyString;
451}
452
453
455{
456 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y3 );
457
458 if( linScale )
459 return linScale->GetUnits();
460 else
461 return wxEmptyString;
462}
463
464
465void SIM_PLOT_PANEL::updateAxes( int aNewTraceType )
466{
467 switch( GetType() )
468 {
469 case ST_AC:
470 if( !m_axis_x )
471 {
472 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
475
476 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "dBV" ), mpALIGN_LEFT );
479
480 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "°" ), mpALIGN_RIGHT );
484 }
485
486 m_axis_x->SetName( _( "Frequency" ) );
487 m_axis_y1->SetName( _( "Gain" ) );
488 m_axis_y2->SetName( _( "Phase" ) );
489 break;
490
491 case ST_DC:
492 prepareDCAxes( aNewTraceType );
493 break;
494
495 case ST_NOISE:
496 if( !m_axis_x )
497 {
498 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
501
502 m_axis_y1 = new mpScaleY( wxEmptyString, mpALIGN_LEFT, false );
505 }
506
507 m_axis_x->SetName( _( "Frequency" ) );
508 m_axis_y1->SetName( _( "noise [(V or A)^2/Hz]" ) );
509 break;
510
511 case ST_TRANSIENT:
512 if( !m_axis_x )
513 {
514 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "s" ), mpALIGN_BOTTOM );
517
518 m_axis_y1 = new LIN_SCALE<mpScaleY>(wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
521
522 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
526 }
527
528 m_axis_x->SetName( _( "Time" ) );
529 m_axis_y1->SetName( _( "Voltage" ) );
530 m_axis_y2->SetName( _( "Current" ) );
531
532 if( ( aNewTraceType & SPT_POWER ) && !m_axis_y3 )
533 {
534 m_plotWin->SetMargins( 35, 140, 35, 70 );
535
536 m_axis_y3 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "W" ), mpALIGN_FAR_RIGHT );
540 }
541
542 if( m_axis_y3 )
543 m_axis_y3->SetName( _( "Power" ) );
544
545 break;
546
547 default:
548 // suppress warnings
549 break;
550 }
551}
552
553void SIM_PLOT_PANEL::prepareDCAxes( int aNewTraceType )
554{
555 wxString sim_cmd = GetSimCommand().Lower();
556 wxString rem;
557
558 if( sim_cmd.StartsWith( ".dc", &rem ) )
559 {
560 wxChar ch = 0;
561
562 rem.Trim( false );
563
564 try
565 {
566 ch = rem.GetChar( 0 );
567 }
568 catch( ... )
569 {
570 // Best efforts
571 }
572
573 switch( ch )
574 {
575 // Make sure that we have a reliable default (even if incorrectly labeled)
576 default:
577 case 'v':
578 if( !m_axis_x )
579 {
580 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "V" ), mpALIGN_BOTTOM );
583 }
584
585 m_axis_x->SetName( _( "Voltage (swept)" ) );
586 break;
587
588 case 'i':
589 if( !m_axis_x )
590 {
591 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "A" ), mpALIGN_BOTTOM );
594 }
595
596 m_axis_x->SetName( _( "Current (swept)" ) );
597 break;
598
599 case 'r':
600 if( !m_axis_x )
601 {
602 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "Ω" ), mpALIGN_BOTTOM );
605 }
606
607 m_axis_x->SetName( _( "Resistance (swept)" ) );
608 break;
609
610 case 't':
611 if( !m_axis_x )
612 {
613 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "°C" ), mpALIGN_BOTTOM );
616 }
617
618 m_axis_x->SetName( _( "Temperature (swept)" ) );
619 break;
620 }
621
622 if( !m_axis_y1 )
623 {
624 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
627 }
628
629 if( !m_axis_y2 )
630 {
631 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
634 }
635
636 m_axis_y1->SetName( _( "Voltage (measured)" ) );
637 m_axis_y2->SetName( _( "Current" ) );
638
639 if( ( aNewTraceType & SPT_POWER ) && !m_axis_y3 )
640 {
641 m_plotWin->SetMargins( 35, 140, 35, 70 );
642
643 m_axis_y3 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "W" ), mpALIGN_FAR_RIGHT );
647 }
648
649 if( m_axis_y3 )
650 m_axis_y3->SetName( _( "Power" ) );
651 }
652}
653
654
656{
657 // Update bg and fg colors:
661
662 // Update color of all traces
663 for( const auto& [ name, trace ] : m_traces )
664 {
665 for( const auto& [ id, cursor ] : trace->GetCursors() )
666 {
667 if( cursor )
669 }
670 }
671
673}
674
675
677{
678 updateAxes();
680}
681
682
684{
685 int type = trace->GetType();
686 wxPenStyle penStyle = ( ( ( type & SPT_AC_PHASE ) || ( type & SPT_CURRENT ) ) && m_dotted_cp )
687 ? wxPENSTYLE_DOT
688 : wxPENSTYLE_SOLID;
689 trace->SetPen( wxPen( trace->GetTraceColour(), 2, penStyle ) );
690}
691
692
693TRACE* SIM_PLOT_PANEL::AddTrace( const wxString& aVectorName, int aType )
694{
695 TRACE* trace = GetTrace( aVectorName, aType );
696
697 if( !trace )
698 {
699 updateAxes( aType );
700
701 if( GetType() == ST_TRANSIENT || GetType() == ST_DC )
702 {
703 bool hasVoltageTraces = false;
704
705 for( const auto& [ id, candidate ] : m_traces )
706 {
707 if( candidate->GetType() & SPT_VOLTAGE )
708 {
709 hasVoltageTraces = true;
710 break;
711 }
712 }
713
714 if( !hasVoltageTraces )
715 {
716 if( m_axis_y2 )
717 m_axis_y2->SetMasterScale( nullptr );
718
719 if( m_axis_y3 )
720 m_axis_y3->SetMasterScale( nullptr );
721 }
722 }
723
724 trace = new TRACE( aVectorName, (SIM_TRACE_TYPE) aType );
726 UpdateTraceStyle( trace );
727 m_traces[ getTraceId( aVectorName, aType ) ] = trace;
728
729 m_plotWin->AddLayer( (mpLayer*) trace );
730 }
731
732 return trace;
733}
734
735
736void SIM_PLOT_PANEL::SetTraceData( TRACE* trace, unsigned int aPoints, const double* aX,
737 const double* aY )
738{
739 std::vector<double> tmp( aY, aY + aPoints );
740
741 if( GetType() == ST_AC )
742 {
743 if( trace->GetType() & SPT_AC_PHASE )
744 {
745 for( unsigned int i = 0; i < aPoints; i++ )
746 tmp[i] = tmp[i] * 180.0 / M_PI; // convert to degrees
747 }
748 else
749 {
750 for( unsigned int i = 0; i < aPoints; i++ )
751 {
752 // log( 0 ) is not valid.
753 if( tmp[i] != 0 )
754 tmp[i] = 20 * log( tmp[i] ) / log( 10.0 ); // convert to dB
755 }
756 }
757 }
758
759 trace->SetData( std::vector<double>( aX, aX + aPoints ), tmp );
760
761 if( ( trace->GetType() & SPT_AC_PHASE ) || ( trace->GetType() & SPT_CURRENT ) )
762 trace->SetScale( m_axis_x, m_axis_y2 );
763 else if( trace->GetType() & SPT_POWER )
764 trace->SetScale( m_axis_x, m_axis_y3 );
765 else
766 trace->SetScale( m_axis_x, m_axis_y1 );
767
768 for( auto& [ cursorId, cursor ] : trace->GetCursors() )
769 {
770 if( cursor )
771 cursor->SetCoordX( cursor->GetCoords().x );
772 }
773
775}
776
777
779{
780 for( const auto& [ name, trace ] : m_traces )
781 {
782 if( trace == aTrace )
783 {
784 m_traces.erase( name );
785 break;
786 }
787 }
788
789 for( const auto& [ id, cursor ] : aTrace->GetCursors() )
790 {
791 if( cursor )
792 m_plotWin->DelLayer( cursor, true );
793 }
794
795 m_plotWin->DelLayer( aTrace, true, true );
796 ResetScales();
797}
798
799
800bool SIM_PLOT_PANEL::DeleteTrace( const wxString& aVectorName, int aTraceType )
801{
802 if( TRACE* trace = GetTrace( aVectorName, aTraceType ) )
803 {
804 DeleteTrace( trace );
805 return true;
806 }
807
808 return false;
809}
810
811
812void SIM_PLOT_PANEL::EnableCursor( const wxString& aVectorName, int aType, int aCursorId,
813 bool aEnable, const wxString& aSignalName )
814{
815 TRACE* t = GetTrace( aVectorName, aType );
816
817 if( t == nullptr || t->HasCursor( aCursorId ) == aEnable )
818 return;
819
820 if( aEnable )
821 {
822 CURSOR* cursor = new CURSOR( t, this );
823 mpWindow* win = GetPlotWin();
824 int width = win->GetXScreen() - win->GetMarginLeft() - win->GetMarginRight();
825 int center = win->GetMarginLeft() + KiROUND( width * ( aCursorId == 1 ? 0.4 : 0.6 ) );
826
827 cursor->SetName( aSignalName );
828 cursor->SetX( center );
830
831 t->SetCursor( aCursorId, cursor );
833 }
834 else
835 {
836 CURSOR* cursor = t->GetCursor( aCursorId );
837 t->SetCursor( aCursorId, nullptr );
838 m_plotWin->DelLayer( cursor, true );
839 }
840
841 // Notify the parent window about the changes
842 wxQueueEvent( GetParent(), new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
843}
844
845
847{
848 if( m_axis_x )
850
851 if( m_axis_y1 )
853
854 if( m_axis_y2 )
856
857 if( m_axis_y3 )
859
860 for( auto& [ name, trace ] : m_traces )
861 trace->UpdateScales();
862}
863
864
865wxDEFINE_EVENT( EVT_SIM_CURSOR_UPDATE, wxCommandEvent );
const char * name
Definition: DXF_plotter.cpp:56
The SIMULATOR_FRAME holds the main user-interface for running simulations.
mpWindow * m_window
wxString getID()
wxRealPoint m_coords
bool m_updateRef
static constexpr int DRAG_MARGIN
bool Inside(const wxPoint &aPoint) const override
Checks whether a point is inside the info box rectangle.
void doSetCoordX(double aValue)
void SetCoordX(double aValue)
void UpdateReference() override
Updates the rectangle reference point.
bool m_updateRequired
TRACE * m_trace
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot method.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxColour ToColour() const
Definition: color4d.cpp:219
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition: color4d.h:294
wxString GetUnits() const
LIN_SCALE(const wxString &name, const wxString &unit, int flags)
const wxString m_unit
void formatLabels() override
LOG_SCALE(const wxString &name, const wxString &unit, int flags)
void formatLabels() override
wxString GetUnits() const
const wxString m_unit
SIM_TYPE GetType() const
const wxString & GetSimCommand() const
wxColour GenerateColor(std::map< wxString, TRACE * > aTraces)
wxColour GetPlotColor(enum COLOR_SET aColorId)
mpScaleY * m_axis_y1
mpScaleY * m_axis_y3
wxBoxSizer * m_sizer
SIM_PLOT_COLORS m_colors
mpInfoLegend * m_legend
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void ResetScales()
Update trace line style.
wxString GetUnitsX() const
void updateAxes(int aNewTraceType=SIM_TRACE_TYPE::SPT_UNKNOWN)
TRACE * AddTrace(const wxString &aVectorName, int aType)
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
TRACE * GetTrace(const wxString &aVecName, int aType) const
virtual ~SIM_PLOT_PANEL()
wxString GetUnitsY3() const
wxString getTraceId(const wxString &aVectorName, int aType) const
Construct the plot axes for DC simulation plot.
void OnLanguageChanged() override
Getter for math plot window.
void EnableCursor(const wxString &aVectorName, int aType, int aCursorId, bool aEnable, const wxString &aSignalName)
Reset scale ranges to fit the current traces.
std::map< wxString, TRACE * > m_traces
mpScaleXBase * m_axis_x
void prepareDCAxes(int aNewTraceType)
Create/Ensure axes are available for plotting.
mpWindow * m_plotWin
void SetTraceData(TRACE *aTrace, unsigned int aPoints, const double *aX, const double *aY)
wxString GetUnitsY2() const
SIM_PLOT_PANEL(const wxString &aCommand, int aOptions, wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxPanelNameStr)
mpWindow * GetPlotWin() const
mpScaleY * m_axis_y2
wxString GetUnitsY1() const
void SetTraceColour(const wxColour &aColour)
std::map< int, CURSOR * > & GetCursors()
SIM_TRACE_TYPE GetType() const
bool HasCursor(int aCursorId)
void SetData(const std::vector< double > &aX, const std::vector< double > &aY) override
Assigns new data set for the trace.
void SetCursor(int aCursorId, CURSOR *aCursor)
const std::vector< double > & GetDataY() const
wxColour GetTraceColour() const
const std::vector< double > & GetDataX() const
CURSOR * GetCursor(int aCursorId)
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
Definition: mathplot.cpp:3589
double y2s(double y) const
Definition: mathplot.cpp:3626
double x2s(double x) const
Definition: mathplot.cpp:3620
double s2x(double plotCoordX) const
Definition: mathplot.cpp:3608
wxPoint m_reference
Definition: mathplot.h:407
wxRect m_dim
Definition: mathplot.h:406
virtual void Move(wxPoint delta)
Moves the layer rectangle of given pixel deltas.
Definition: mathplot.cpp:135
Implements the legend to be added to the plot This layer allows you to add a legend to describe the p...
Definition: mathplot.h:449
bool m_drawOutsideMargins
Definition: mathplot.h:330
bool m_continuous
Definition: mathplot.h:328
void SetPen(wxPen pen)
Set layer pen.
Definition: mathplot.h:286
bool m_visible
Definition: mathplot.h:332
virtual void SetName(const wxString &name)
Set layer name.
Definition: mathplot.h:276
const wxPen & GetPen() const
Get pen set for this layer.
Definition: mathplot.h:257
void SetVisible(bool show)
Sets layer visibility.
Definition: mathplot.h:311
void SetNameAlign(int align)
Definition: mathplot.h:722
void ResetDataRange()
Definition: mathplot.h:772
Plot layer implementing a y-scale ruler.
Definition: mathplot.h:955
void SetMasterScale(mpScaleY *masterScale)
Definition: mathplot.h:992
Canvas for plotting mpLayer implementations.
Definition: mathplot.h:1060
void SetColourTheme(const wxColour &bgColour, const wxColour &drawColour, const wxColour &axesColour)
Set Color theme.
Definition: mathplot.cpp:2812
int GetMarginLeft() const
Definition: mathplot.h:1390
int GetScrX(void) const
Get current view's X dimension in device context units.
Definition: mathplot.h:1151
void SetMargins(int top, int right, int bottom, int left)
Set window margins, creating a blank area where some kinds of layers cannot draw.
Definition: mathplot.cpp:2746
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition: mathplot.h:1205
int GetXScreen(void) const
Definition: mathplot.h:1152
void LimitView(bool aEnable)
Limit zooming & panning to the area used by the plots.
Definition: mathplot.h:1442
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1215
int GetMarginTop() const
Definition: mathplot.h:1384
bool DelLayer(mpLayer *layer, bool alsoDeleteObject=false, bool refreshDisplay=true)
Remove a plot layer from the canvas.
Definition: mathplot.cpp:2330
void UpdateAll()
Refresh display.
Definition: mathplot.cpp:2468
wxCoord y2p(double y)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition: mathplot.h:1220
int GetMarginRight() const
Definition: mathplot.h:1386
int GetMarginBottom() const
Definition: mathplot.h:1388
void EnableDoubleBuffer(bool enabled)
Enable/disable the double-buffering of the window, eliminating the flicker (default=disabled).
Definition: mathplot.h:1225
int GetScrY(void) const
Get current view's Y dimension in device context units.
Definition: mathplot.h:1160
bool AddLayer(mpLayer *layer, bool refreshDisplay=true)
Add a plot layer to the canvas.
Definition: mathplot.cpp:2314
#define _(s)
#define mpALIGN_RIGHT
Aligns label to the right.
Definition: mathplot.h:488
#define mpALIGN_FAR_RIGHT
Aligns label to the right of mpALIGN_RIGHT.
Definition: mathplot.h:502
#define mpALIGN_LEFT
Aligns label to the left.
Definition: mathplot.h:492
#define mpALIGN_BOTTOM
Aligns label to the bottom.
Definition: mathplot.h:496
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
Class is responsible for providing colors for traces on simulation plot.
wxDEFINE_EVENT(EVT_SIM_CURSOR_UPDATE, wxCommandEvent)
static void getSISuffix(double x, const wxString &unit, int &power, wxString &suffix)
static int countDecimalDigits(double x, int maxDigits)
static wxString formatFloat(double x, int nDigits)
SIM_TRACE_TYPE
Definition: sim_types.h:47
@ SPT_AC_PHASE
Definition: sim_types.h:51
@ SPT_VOLTAGE
Definition: sim_types.h:49
@ SPT_POWER
Definition: sim_types.h:53
@ SPT_CURRENT
Definition: sim_types.h:50
@ ST_TRANSIENT
Definition: sim_types.h:42
@ ST_NOISE
Definition: sim_types.h:37
@ ST_AC
Definition: sim_types.h:34
@ ST_DC
Definition: sim_types.h:35
constexpr int delta
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85