KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_plot_tab.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 The 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 <wx/tokenzr.h>
29#include "sim_plot_colors.h"
30#include "sim_plot_tab.h"
31#include "simulator_frame.h"
32#include "core/kicad_algo.h"
33
34#include <algorithm>
35#include <cmath>
36#include <limits>
37
38
39static wxString formatFloat( double x, int nDigits )
40{
41 wxString rv, fmt;
42
43 if( nDigits )
44 fmt.Printf( "%%.0%df", nDigits );
45 else
46 fmt = wxT( "%.0f" );
47
48 rv.Printf( fmt, x );
49
50 return rv;
51}
52
53
54static void getSISuffix( double x, const wxString& unit, int& power, wxString& suffix )
55{
56 const int n_powers = 11;
57
58 const struct
59 {
60 int exponent;
61 char suffix;
62 } powers[] =
63 {
64 { -18, 'a' },
65 { -15, 'f' },
66 { -12, 'p' },
67 { -9, 'n' },
68 { -6, 'u' },
69 { -3, 'm' },
70 { 0, 0 },
71 { 3, 'k' },
72 { 6, 'M' },
73 { 9, 'G' },
74 { 12, 'T' },
75 { 14, 'P' }
76 };
77
78 power = 0;
79 suffix = unit;
80
81 if( x == 0.0 )
82 return;
83
84 for( int i = 0; i < n_powers - 1; i++ )
85 {
86 double r_cur = pow( 10, powers[i].exponent );
87
88 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
89 {
90 power = powers[i].exponent;
91
92 if( powers[i].suffix )
93 suffix = wxString( powers[i].suffix ) + unit;
94 else
95 suffix = unit;
96
97 return;
98 }
99 }
100}
101
102
103static int countDecimalDigits( double x, int maxDigits )
104{
105 if( std::isnan( x ) )
106 return 0;
107
108 auto countSignificantDigits =
109 [&]( int64_t k )
110 {
111 while( k && ( k % 10LL ) == 0LL )
112 k /= 10LL;
113
114 int n = 0;
115
116 while( k != 0LL )
117 {
118 n++;
119 k /= 10LL;
120 }
121
122 return n;
123 };
124
125 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (double) maxDigits ) );
126 int n = countSignificantDigits( k );
127
128 // check for trailing 9's
129 n = std::min( n, countSignificantDigits( k + 1 ) );
130
131 return n;
132}
133
134
135template <typename T_PARENT>
136class LIN_SCALE : public T_PARENT
137{
138public:
139 LIN_SCALE( const wxString& name, const wxString& unit, int flags ) :
140 T_PARENT( name, flags, false ),
141 m_unit( unit )
142 {};
143
144 wxString GetUnits() const { return m_unit; }
145
146private:
147 void formatLabels() override
148 {
149 double maxVis = T_PARENT::AbsVisibleMaxValue();
150
151 wxString suffix;
152 int power = 0;
153 int digits = 0;
154 int constexpr MAX_DIGITS = 3;
155 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
156 bool duplicateLabels = false;
157
158 getSISuffix( maxVis, m_unit, power, suffix );
159
160 double sf = pow( 10.0, power );
161
162 for( mpScaleBase::TICK_LABEL& l : T_PARENT::m_tickLabels )
163 digits = std::max( digits, countDecimalDigits( l.pos / sf, MAX_DIGITS ) );
164
165 do
166 {
167 for( size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
168 {
169 mpScaleBase::TICK_LABEL& l = T_PARENT::m_tickLabels[ii];
170
171 l.label = formatFloat( l.pos / sf, digits );
172 l.visible = true;
173
174 if( ii > 0 && l.label == T_PARENT::m_tickLabels[ii-1].label )
175 duplicateLabels = true;
176 }
177 }
178 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
179
180 if( m_base_axis_label.IsEmpty() )
181 m_base_axis_label = T_PARENT::GetName();
182
183 T_PARENT::SetName( wxString::Format( "%s (%s)", m_base_axis_label, suffix ) );
184 }
185
186private:
187 const wxString m_unit;
189};
190
191
192class TIME_SCALE : public LIN_SCALE<mpScaleX>
193{
194public:
195 TIME_SCALE( const wxString& name, const wxString& unit, int flags ) :
196 LIN_SCALE( name, unit, flags ),
197 m_startTime( 0.0 ),
198 m_endTime( 1.0 )
199 {};
200
201 void ExtendDataRange( double minV, double maxV ) override
202 {
203 LIN_SCALE::ExtendDataRange( minV, maxV );
204
205 // Time is never longer than the simulation itself
206 if( m_minV < m_startTime )
208
209 if( m_maxV > m_endTime )
211 };
212
213 void SetStartAndEnd( double aStartTime, double aEndTime )
214 {
215 m_startTime = aStartTime;
216 m_endTime = aEndTime;
218 }
219
220 void ResetDataRange() override
221 {
224 m_rangeSet = true;
225 }
226
227protected:
229 double m_endTime;
230};
231
232
233template <typename T_PARENT>
234class LOG_SCALE : public T_PARENT
235{
236public:
237 LOG_SCALE( const wxString& name, const wxString& unit, int flags ) :
238 T_PARENT( name, flags, false ),
239 m_unit( unit )
240 {};
241
242 wxString GetUnits() const { return m_unit; }
243
244private:
245 void formatLabels() override
246 {
247 wxString suffix;
248 int power;
249 int constexpr MAX_DIGITS = 3;
250
251 for( mpScaleBase::TICK_LABEL& l : T_PARENT::m_tickLabels )
252 {
253 getSISuffix( l.pos, m_unit, power, suffix );
254 double sf = pow( 10.0, power );
255 int k = countDecimalDigits( l.pos / sf, MAX_DIGITS );
256
257 l.label = formatFloat( l.pos / sf, k ) + suffix;
258 l.visible = true;
259 }
260 }
261
262private:
263 const wxString m_unit;
264};
265
266
267void CURSOR::SetCoordX( double aValue )
268{
269 wxRealPoint oldCoords = m_coords;
270
271 doSetCoordX( aValue );
272 m_updateRequired = false;
273 m_updateRef = true;
274
275 if( m_window )
276 {
277 wxRealPoint delta = m_coords - oldCoords;
278 mpInfoLayer::Move( wxPoint( m_window->x2p( m_trace->x2s( delta.x ) ),
279 m_window->y2p( m_trace->y2s( delta.y ) ) ) );
280
281 m_window->Refresh();
282 }
283}
284
285
286void CURSOR::Move( wxPoint aDelta )
287{
288 Update();
289
290 if( m_trace->IsMultiRun() && m_window && m_trace->GetSweepCount() > 1
291 && m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
292 {
293 int newY = m_reference.y + aDelta.y;
294
295 double plotY = m_window->p2y( newY );
296 m_snapTargetY = m_trace->s2y( plotY );
297 m_snapToNearest = true;
298 }
299
300 mpInfoLayer::Move( aDelta );
301}
302
303
304bool CURSOR::OnDoubleClick( const wxPoint& aPoint, mpWindow& aWindow )
305{
306 if( !Inside( aPoint ) )
307 return false;
308
309 if( !m_trace->IsMultiRun() )
310 return false;
311
312 int sweepCount = m_trace->GetSweepCount();
313 size_t sweepSize = m_trace->GetSweepSize();
314
315 if( sweepCount <= 1 )
316 return false;
317
318 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
319 return false;
320
321 if( m_sweepIndex < 0 || m_sweepIndex >= sweepCount )
322 m_sweepIndex = 0;
323
324 m_sweepIndex = ( m_sweepIndex + 1 ) % sweepCount;
325
326 Update();
327 m_updateRef = true;
328 m_window = &aWindow;
329 aWindow.Refresh();
330
331 return true;
332}
333
334
335void CURSOR::doSetCoordX( double aValue )
336{
337 m_coords.x = aValue;
338
339 const std::vector<double>& dataX = m_trace->GetDataX();
340 const std::vector<double>& dataY = m_trace->GetDataY();
341
342 if( dataX.size() <= 1 )
343 return;
344
345 bool snapToNearest = m_snapToNearest;
346 double snapTargetY = m_snapTargetY;
347 m_snapToNearest = false;
348
349 size_t startIdx = 0;
350 size_t endIdx = dataX.size();
351 int sweepCount = m_trace->GetSweepCount();
352 size_t sweepSize = m_trace->GetSweepSize();
353
354 if( snapToNearest && m_trace->IsMultiRun() && sweepCount > 1
355 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0
356 && std::isfinite( snapTargetY ) )
357 {
358 double bestDistance = std::numeric_limits<double>::infinity();
359 int bestSweep = m_sweepIndex;
360 bool found = false;
361
362 for( int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
363 {
364 size_t candidateStart = static_cast<size_t>( sweepIdx ) * sweepSize;
365 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
366
367 if( candidateStart >= candidateEnd )
368 continue;
369
370 auto candidateBegin = dataX.begin() + candidateStart;
371 auto candidateEndIt = dataX.begin() + candidateEnd;
372 auto candidateMaxIt = std::upper_bound( candidateBegin, candidateEndIt, m_coords.x );
373 int candidateMaxIdx = candidateMaxIt - dataX.begin();
374 int candidateMinIdx = candidateMaxIdx - 1;
375
376 if( candidateMinIdx < (int) candidateStart
377 || candidateMaxIdx >= (int) candidateEnd
378 || candidateMaxIdx >= (int) dataX.size() )
379 {
380 continue;
381 }
382
383 double leftX = dataX[candidateMinIdx];
384 double rightX = dataX[candidateMaxIdx];
385
386 if( leftX == rightX )
387 continue;
388
389 double leftY = dataY[candidateMinIdx];
390 double rightY = dataY[candidateMaxIdx];
391 double value = leftY + ( rightY - leftY ) / ( rightX - leftX ) * ( m_coords.x - leftX );
392 double distance = std::fabs( value - snapTargetY );
393
394 if( distance < bestDistance )
395 {
396 bestDistance = distance;
397 bestSweep = sweepIdx;
398 found = true;
399 }
400 }
401
402 if( found )
403 m_sweepIndex = bestSweep;
404 }
405
406 if( m_trace->IsMultiRun() && sweepCount > 1
407 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0 )
408 {
409 size_t available = static_cast<size_t>( sweepCount ) * sweepSize;
410
411 if( available <= dataX.size() )
412 {
413 if( m_sweepIndex < 0 || m_sweepIndex >= sweepCount )
414 m_sweepIndex = std::max( sweepCount - 1, 0 );
415
416 startIdx = static_cast<size_t>( m_sweepIndex ) * sweepSize;
417 endIdx = std::min( dataX.size(), startIdx + sweepSize );
418 }
419 else
420 {
421 m_sweepIndex = 0;
422 }
423 }
424 else
425 {
426 m_sweepIndex = 0;
427 }
428
429 if( startIdx >= endIdx )
430 {
431 m_coords.y = NAN;
432 return;
433 }
434
435 auto beginIt = dataX.begin() + startIdx;
436 auto endIt = dataX.begin() + endIdx;
437
438 // Find the closest point coordinates
439 auto maxXIt = std::upper_bound( beginIt, endIt, m_coords.x );
440 int maxIdx = maxXIt - dataX.begin();
441 int minIdx = maxIdx - 1;
442
443 // Out of bounds checks
444 if( minIdx < (int) startIdx || maxIdx >= (int) endIdx || maxIdx >= (int) dataX.size() )
445 {
446 // Simulation may not be complete yet, or we may have a cursor off the beginning or end
447 // of the data. Either way, that's where the user put it. Don't second guess them; just
448 // leave its y value undefined.
449 m_coords.y = NAN;
450 return;
451 }
452
453 const double leftX = dataX[minIdx];
454 const double rightX = dataX[maxIdx];
455 const double leftY = dataY[minIdx];
456 const double rightY = dataY[maxIdx];
457
458 // Linear interpolation
459 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * ( m_coords.x - leftX );
460}
461
462
464{
465 for( const auto& [ id, cursor ] : m_trace->GetCursors() )
466 {
467 if( cursor == this )
468 return wxString::Format( _( "%d" ), id );
469 }
470
471 return wxEmptyString;
472}
473
474
475void CURSOR::Plot( wxDC& aDC, mpWindow& aWindow )
476{
477 if( !m_window )
478 m_window = &aWindow;
479
480 if( !m_visible || m_trace->GetDataX().size() <= 1 )
481 return;
482
483 if( m_updateRequired )
484 {
485 doSetCoordX( m_trace->s2x( aWindow.p2x( m_dim.x ) ) );
486 m_updateRequired = false;
487
488 // Notify the parent window about the changes
489 wxQueueEvent( aWindow.GetParent(), new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
490 }
491 else
492 {
493 m_updateRef = true;
494 }
495
496 if( m_updateRef )
497 {
499 m_updateRef = false;
500 }
501
502 // Line length in horizontal and vertical dimensions
503 const wxPoint cursorPos( aWindow.x2p( m_trace->x2s( m_coords.x ) ),
504 aWindow.y2p( m_trace->y2s( m_coords.y ) ) );
505
506 wxCoord leftPx = aWindow.GetMarginLeft();
507 wxCoord rightPx = aWindow.GetScrX() - aWindow.GetMarginRight();
508 wxCoord topPx = aWindow.GetMarginTop();
509 wxCoord bottomPx = aWindow.GetScrY() - aWindow.GetMarginBottom();
510
511 wxPen pen = GetPen();
512 wxColour fg = aWindow.GetForegroundColour();
513 COLOR4D cursorColor = COLOR4D( m_trace->GetTraceColour() ).Mix( fg, 0.6 );
514 COLOR4D textColor = fg;
515
516 if( cursorColor.Distance( textColor ) < 0.66 )
517 textColor.Invert();
518
519 pen.SetColour( cursorColor.ToColour() );
520 pen.SetStyle( m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
521 aDC.SetPen( pen );
522
523 if( topPx < cursorPos.y && cursorPos.y < bottomPx )
524 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
525
526 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
527 {
528 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
529
530 wxString id = getID();
531 wxSize size = aDC.GetTextExtent( wxS( "M" ) );
532 wxRect textRect( wxPoint( cursorPos.x + 1 - size.x / 2, topPx - 4 - size.y ), size );
533 wxBrush brush;
534 wxPoint poly[3];
535
536 // Because a "1" looks off-center if it's actually centred.
537 if( id == "1" )
538 textRect.x -= 1;
539
540 // We want an equalateral triangle, so use size.y for both axes.
541 size.y += 3;
542 // Make sure it's an even number so the slopes of the sides will be identical.
543 size.y = ( size.y / 2 ) * 2;
544 poly[0] = { cursorPos.x - 1 - size.y / 2, topPx - size.y };
545 poly[1] = { cursorPos.x + 1 + size.y / 2, topPx - size.y };
546 poly[2] = { cursorPos.x, topPx };
547
548 brush.SetStyle( wxBRUSHSTYLE_SOLID );
549 brush.SetColour( m_trace->GetTraceColour() );
550 aDC.SetBrush( brush );
551 aDC.DrawPolygon( 3, poly );
552
553 aDC.SetTextForeground( textColor.ToColour() );
554 aDC.DrawLabel( id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
555
556 if( m_trace->IsMultiRun() && m_trace->GetSweepCount() > 1
557 && m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
558 {
559 wxString runLabel;
560 const std::vector<wxString>& labels = m_trace->GetMultiRunLabels();
561
562 if( m_sweepIndex >= 0 && m_sweepIndex < (int) labels.size() )
563 {
564 runLabel = labels[m_sweepIndex];
565 }
566 else
567 {
568 runLabel = wxString::Format( _( "Run %d" ), m_sweepIndex + 1 );
569 }
570
571 wxSize runSize = aDC.GetTextExtent( runLabel );
572 int runX = textRect.GetRight() + 6;
573 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
574
575 runRect.Inflate( 3, 1 );
576
577 wxBrush labelBrush( aWindow.GetBackgroundColour() );
578 wxPen labelPen( cursorColor.ToColour() );
579
580 aDC.SetPen( labelPen );
581 aDC.SetBrush( labelBrush );
582 aDC.DrawRectangle( runRect );
583 aDC.SetTextForeground( cursorColor.ToColour() );
584 aDC.DrawLabel( runLabel, runRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
585 }
586 }
587}
588
589
590bool CURSOR::Inside( const wxPoint& aPoint ) const
591{
592 if( !m_window || !m_trace )
593 return false;
594
595 return ( std::abs( (double) aPoint.x -
596 m_window->x2p( m_trace->x2s( m_coords.x ) ) ) <= DRAG_MARGIN )
597 || ( std::abs( (double) aPoint.y -
598 m_window->y2p( m_trace->y2s( m_coords.y ) ) ) <= DRAG_MARGIN );
599}
600
601
603{
604 if( !m_window )
605 return;
606
607 m_reference.x = m_window->x2p( m_trace->x2s( m_coords.x ) );
608 m_reference.y = m_window->y2p( m_trace->y2s( m_coords.y ) );
609}
610
611
612SIM_PLOT_TAB::SIM_PLOT_TAB( const wxString& aSimCommand, wxWindow* parent ) :
613 SIM_TAB( aSimCommand, parent ),
614 m_axis_x( nullptr ),
615 m_axis_y1( nullptr ),
616 m_axis_y2( nullptr ),
617 m_axis_y3( nullptr ),
618 m_dotted_cp( false )
619{
620 m_sizer = new wxBoxSizer( wxVERTICAL );
621 m_plotWin = new mpWindow( this, wxID_ANY );
622
623 m_plotWin->LimitView( true );
624 m_plotWin->SetMargins( 30, 70, 45, 70 );
626
627 updateAxes();
628
629 // a mpInfoLegend displays le name of traces on the left top panel corner:
630 m_legend = new mpInfoLegend( wxRect( 0, 0, 200, 40 ), wxTRANSPARENT_BRUSH );
631 m_legend->SetVisible( false );
632 m_plotWin->AddLayer( m_legend );
633 m_LastLegendPosition = m_legend->GetPosition();
634
635 m_plotWin->EnableDoubleBuffer( true );
636 m_plotWin->UpdateAll();
637
638 m_sizer->Add( m_plotWin, 1, wxALL | wxEXPAND, 1 );
639 SetSizer( m_sizer );
640}
641
642
644{
645 // ~mpWindow destroys all the added layers, so there is no need to destroy m_traces contents
646}
647
648
649void SIM_PLOT_TAB::SetY1Scale( bool aLock, double aMin, double aMax )
650{
651 wxCHECK( m_axis_y1, /* void */ );
652 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
653}
654
655
656void SIM_PLOT_TAB::SetY2Scale( bool aLock, double aMin, double aMax )
657{
658 wxCHECK( m_axis_y2, /* void */ );
659 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
660}
661
662
663void SIM_PLOT_TAB::SetY3Scale( bool aLock, double aMin, double aMax )
664{
665 wxCHECK( m_axis_y3, /* void */ );
666 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
667}
668
669
671{
672 LOG_SCALE<mpScaleXLog>* logScale = dynamic_cast<LOG_SCALE<mpScaleXLog>*>( m_axis_x );
673 LIN_SCALE<mpScaleX>* linScale = dynamic_cast<LIN_SCALE<mpScaleX>*>( m_axis_x );
674
675 if( logScale )
676 return logScale->GetUnits();
677 else if( linScale )
678 return linScale->GetUnits();
679 else
680 return wxEmptyString;
681}
682
683
685{
686 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y1 );
687
688 if( linScale )
689 return linScale->GetUnits();
690 else
691 return wxEmptyString;
692}
693
694
696{
697 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y2 );
698
699 if( linScale )
700 return linScale->GetUnits();
701 else
702 return wxEmptyString;
703}
704
705
707{
708 LIN_SCALE<mpScaleY>* linScale = dynamic_cast<LIN_SCALE<mpScaleY>*>( m_axis_y3 );
709
710 if( linScale )
711 return linScale->GetUnits();
712 else
713 return wxEmptyString;
714}
715
716
717void SIM_PLOT_TAB::updateAxes( int aNewTraceType )
718{
719 switch( GetSimType() )
720 {
721 case ST_AC:
722 if( !m_axis_x )
723 {
724 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
725 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
726 m_plotWin->AddLayer( m_axis_x );
727
728 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "dB" ), mpALIGN_LEFT );
729 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
730 m_plotWin->AddLayer( m_axis_y1 );
731
732 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "°" ), mpALIGN_RIGHT );
733 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
734 m_axis_y2->SetMasterScale( m_axis_y1 );
735 m_plotWin->AddLayer( m_axis_y2 );
736 }
737
738 m_axis_x->SetName( _( "Frequency" ) );
739 m_axis_y1->SetName( _( "Gain" ) );
740 m_axis_y2->SetName( _( "Phase" ) );
741 break;
742
743 case ST_SP:
744 if( !m_axis_x )
745 {
746 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
747 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
748 m_plotWin->AddLayer( m_axis_x );
749
750 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "" ), mpALIGN_LEFT );
751 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
752 m_plotWin->AddLayer( m_axis_y1 );
753
754 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "°" ), mpALIGN_RIGHT );
755 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
756 m_axis_y2->SetMasterScale( m_axis_y1 );
757 m_plotWin->AddLayer( m_axis_y2 );
758 }
759
760 m_axis_x->SetName( _( "Frequency" ) );
761 m_axis_y1->SetName( _( "Amplitude" ) );
762 m_axis_y2->SetName( _( "Phase" ) );
763 break;
764
765 case ST_DC:
766 prepareDCAxes( aNewTraceType );
767 break;
768
769 case ST_NOISE:
770 if( !m_axis_x )
771 {
772 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
773 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
774 m_plotWin->AddLayer( m_axis_x );
775
776 if( ( aNewTraceType & SPT_CURRENT ) == 0 )
777 {
778 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "" ), mpALIGN_LEFT );
779 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
780 m_plotWin->AddLayer( m_axis_y1 );
781 }
782 else
783 {
784 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "" ), mpALIGN_RIGHT );
785 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
786 m_plotWin->AddLayer( m_axis_y2 );
787 }
788 }
789
790 m_axis_x->SetName( _( "Frequency" ) );
791
792 if( m_axis_y1 )
793 m_axis_y1->SetName( _( "Noise (V/√Hz)" ) );
794
795 if( m_axis_y2 )
796 m_axis_y2->SetName( _( "Noise (A/√Hz)" ) );
797
798 break;
799
800 case ST_FFT:
801 if( !m_axis_x )
802 {
803 m_axis_x = new LOG_SCALE<mpScaleXLog>( wxEmptyString, wxT( "Hz" ), mpALIGN_BOTTOM );
804 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
805 m_plotWin->AddLayer( m_axis_x );
806
807 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "dB" ), mpALIGN_LEFT );
808 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
809 m_plotWin->AddLayer( m_axis_y1 );
810 }
811
812 m_axis_x->SetName( _( "Frequency" ) );
813 m_axis_y1->SetName( _( "Intensity" ) );
814 break;
815
816 case ST_TRAN:
817 if( !m_axis_x )
818 {
819 m_axis_x = new TIME_SCALE( wxEmptyString, wxT( "s" ), mpALIGN_BOTTOM );
820 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
821 m_plotWin->AddLayer( m_axis_x );
822
823 m_axis_y1 = new LIN_SCALE<mpScaleY>(wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
824 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
825 m_plotWin->AddLayer( m_axis_y1 );
826
827 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
828 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
829 m_axis_y2->SetMasterScale( m_axis_y1 );
830 m_plotWin->AddLayer( m_axis_y2 );
831 }
832
833 m_axis_x->SetName( _( "Time" ) );
834 m_axis_y1->SetName( _( "Voltage" ) );
835 m_axis_y2->SetName( _( "Current" ) );
836
837 if( aNewTraceType & SPT_POWER )
839
840 if( m_axis_y3 )
841 m_axis_y3->SetName( _( "Power" ) );
842
843 break;
844
845 default:
846 // suppress warnings
847 break;
848 }
849
850 if( GetSimType() == ST_TRAN || GetSimType() == ST_DC )
851 {
852 if( m_axis_y3 )
853 {
854 m_plotWin->SetMargins( 30, 160, 45, 70 );
855
856 if( m_axis_y2 )
857 m_axis_y2->SetNameAlign( mpALIGN_BORDER_RIGHT );
858
859 m_axis_y3->SetAlign( mpALIGN_BORDER_RIGHT );
860 m_axis_y3->SetNameAlign( mpALIGN_BORDER_RIGHT );
861 }
862 else
863 {
864 m_plotWin->SetMargins( 30, 70, 45, 70 );
865
866 if( m_axis_y2 )
867 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
868 }
869 }
870
871 if( m_axis_x )
873
874 if( m_axis_y1 )
876
877 if( m_axis_y2 )
879
880 if( m_axis_y3 )
882
884}
885
886
887void SIM_PLOT_TAB::prepareDCAxes( int aNewTraceType )
888{
889 wxString sim_cmd = GetSimCommand().Lower();
890 wxString rem;
891
892 if( sim_cmd.StartsWith( ".dc", &rem ) )
893 {
894 wxChar ch = 0;
895
896 rem.Trim( false );
897
898 try
899 {
900 ch = rem.GetChar( 0 );
901 }
902 catch( ... )
903 {
904 // Best efforts
905 }
906
907 switch( ch )
908 {
909 // Make sure that we have a reliable default (even if incorrectly labeled)
910 default:
911 case 'v':
912 if( !m_axis_x )
913 {
914 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "V" ), mpALIGN_BOTTOM );
915 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
916 m_plotWin->AddLayer( m_axis_x );
917 }
918
919 m_axis_x->SetName( _( "Voltage (swept)" ) );
920 break;
921
922 case 'i':
923 if( !m_axis_x )
924 {
925 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "A" ), mpALIGN_BOTTOM );
926 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
927 m_plotWin->AddLayer( m_axis_x );
928 }
929
930 m_axis_x->SetName( _( "Current (swept)" ) );
931 break;
932
933 case 'r':
934 if( !m_axis_x )
935 {
936 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "Ω" ), mpALIGN_BOTTOM );
937 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
938 m_plotWin->AddLayer( m_axis_x );
939 }
940
941 m_axis_x->SetName( _( "Resistance (swept)" ) );
942 break;
943
944 case 't':
945 if( !m_axis_x )
946 {
947 m_axis_x = new LIN_SCALE<mpScaleX>( wxEmptyString, wxT( "°C" ), mpALIGN_BOTTOM );
948 m_axis_x->SetNameAlign( mpALIGN_BOTTOM );
949 m_plotWin->AddLayer( m_axis_x );
950 }
951
952 m_axis_x->SetName( _( "Temperature (swept)" ) );
953 break;
954 }
955
956 if( !m_axis_y1 )
957 {
958 m_axis_y1 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "V" ), mpALIGN_LEFT );
959 m_axis_y1->SetNameAlign( mpALIGN_LEFT );
960 m_plotWin->AddLayer( m_axis_y1 );
961 }
962
963 if( !m_axis_y2 )
964 {
965 m_axis_y2 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "A" ), mpALIGN_RIGHT );
966 m_axis_y2->SetNameAlign( mpALIGN_RIGHT );
967 m_plotWin->AddLayer( m_axis_y2 );
968 }
969
970 m_axis_y1->SetName( _( "Voltage (measured)" ) );
971 m_axis_y2->SetName( _( "Current" ) );
972
973 if( ( aNewTraceType & SPT_POWER ) )
975
976 if( m_axis_y3 )
977 m_axis_y3->SetName( _( "Power" ) );
978 }
979}
980
981
983{
984 if( !m_axis_y3 )
985 {
986 m_plotWin->SetMargins( 30, 160, 45, 70 );
987 m_axis_y3 = new LIN_SCALE<mpScaleY>( wxEmptyString, wxT( "W" ), mpALIGN_BORDER_RIGHT );
988 m_axis_y3->SetNameAlign( mpALIGN_BORDER_RIGHT );
989 m_axis_y3->SetMasterScale( m_axis_y1 );
990 m_plotWin->AddLayer( m_axis_y3 );
991 }
992
993 if( m_axis_y3 )
994 {
995 m_axis_y3->SetAlign( mpALIGN_BORDER_RIGHT );
996 m_axis_y3->SetNameAlign( mpALIGN_BORDER_RIGHT );
997 }
998
999 if( m_axis_y2 )
1000 m_axis_y2->SetNameAlign( mpALIGN_BORDER_RIGHT );
1001}
1002
1003
1005{
1006 // Update bg and fg colors:
1007 m_plotWin->SetColourTheme( m_colors.GetPlotColor( SIM_PLOT_COLORS::COLOR_SET::BACKGROUND ),
1010
1011 m_plotWin->UpdateAll();
1012}
1013
1014
1016{
1017 updateAxes();
1018 m_plotWin->UpdateAll();
1019}
1020
1021
1023{
1024 int type = trace->GetType();
1025 wxPenStyle penStyle;
1026
1027 if( ( type & SPT_AC_GAIN ) > 0 )
1028 penStyle = wxPENSTYLE_SOLID;
1029 else if( ( type & SPT_AC_PHASE ) > 0 )
1030 penStyle = m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1031 else if( ( type & SPT_CURRENT ) > 0 )
1032 penStyle = m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1033 else
1034 penStyle = wxPENSTYLE_SOLID;
1035
1036 trace->SetPen( wxPen( trace->GetTraceColour(), 2, penStyle ) );
1037 m_sessionTraceColors[ trace->GetName() ] = trace->GetTraceColour();
1038}
1039
1040
1041TRACE* SIM_PLOT_TAB::GetOrAddTrace( const wxString& aVectorName, int aType )
1042{
1043 TRACE* trace = GetTrace( aVectorName, aType );
1044
1045 if( !trace )
1046 {
1047 updateAxes( aType );
1048
1049 if( GetSimType() == ST_TRAN || GetSimType() == ST_DC )
1050 {
1051 bool hasVoltageTraces = false;
1052
1053 for( const auto& [ id, candidate ] : m_traces )
1054 {
1055 if( candidate->GetType() & SPT_VOLTAGE )
1056 {
1057 hasVoltageTraces = true;
1058 break;
1059 }
1060 }
1061
1062 if( !hasVoltageTraces )
1063 {
1064 if( m_axis_y2 )
1065 m_axis_y2->SetMasterScale( nullptr );
1066
1067 if( m_axis_y3 )
1068 m_axis_y3->SetMasterScale( nullptr );
1069 }
1070 }
1071
1072 trace = new TRACE( aVectorName, (SIM_TRACE_TYPE) aType );
1073
1074 if( m_sessionTraceColors.count( aVectorName ) )
1075 trace->SetTraceColour( m_sessionTraceColors[ aVectorName ] );
1076 else
1077 trace->SetTraceColour( m_colors.GenerateColor( m_sessionTraceColors ) );
1078
1079 UpdateTraceStyle( trace );
1080 m_traces[ getTraceId( aVectorName, aType ) ] = trace;
1081
1082 m_plotWin->AddLayer( (mpLayer*) trace );
1083 }
1084
1085 return trace;
1086}
1087
1088
1089void SIM_PLOT_TAB::SetTraceData( TRACE* trace, std::vector<double>& aX, std::vector<double>& aY,
1090 int aSweepCount, size_t aSweepSize, bool aIsMultiRun,
1091 const std::vector<wxString>& aMultiRunLabels )
1092{
1093 if( dynamic_cast<LOG_SCALE<mpScaleXLog>*>( m_axis_x ) )
1094 {
1095 // log( 0 ) is not valid.
1096 if( aX.size() > 0 && aX[0] == 0 )
1097 {
1098 aX.erase( aX.begin() );
1099 aY.erase( aY.begin() );
1100 }
1101 }
1102
1103 if( GetSimType() == ST_AC || GetSimType() == ST_FFT )
1104 {
1105 if( trace->GetType() & SPT_AC_PHASE )
1106 {
1107 for( double& pt : aY )
1108 pt = pt * 180.0 / M_PI; // convert to degrees
1109 }
1110 else
1111 {
1112 for( double& pt : aY )
1113 {
1114 // log( 0 ) is not valid.
1115 if( pt != 0 )
1116 pt = 20 * log( pt ) / log( 10.0 ); // convert to dB
1117 }
1118 }
1119 }
1120
1121 trace->SetData( aX, aY );
1122 trace->SetSweepCount( aSweepCount );
1123 trace->SetSweepSize( aSweepSize );
1124 trace->SetIsMultiRun( aIsMultiRun );
1125 trace->SetMultiRunLabels( aMultiRunLabels );
1126
1127 // Phase and currents on second Y axis, except for AC currents, those use the same axis as voltage
1128 if( ( trace->GetType() & SPT_AC_PHASE )
1129 || ( ( GetSimType() != ST_AC ) && ( trace->GetType() & SPT_CURRENT ) ) )
1130 {
1131 trace->SetScale( m_axis_x, m_axis_y2 );
1132 }
1133 else if( trace->GetType() & SPT_POWER )
1134 {
1135 trace->SetScale( m_axis_x, m_axis_y3 );
1136 }
1137 else
1138 {
1139 trace->SetScale( m_axis_x, m_axis_y1 );
1140 }
1141
1142 for( auto& [ cursorId, cursor ] : trace->GetCursors() )
1143 {
1144 if( cursor )
1145 cursor->SetCoordX( cursor->GetCoords().x );
1146 }
1147
1149}
1150
1151
1153{
1154 bool hasY1Traces = false;
1155 bool hasY2Traces = false;
1156 bool hasY3Traces = false;
1157
1158 for( const auto& [ name, trace ] : m_traces )
1159 {
1160 if( !trace )
1161 continue;
1162
1163 if( trace->GetType() & SPT_POWER )
1164 {
1165 hasY3Traces = true;
1166 }
1167 else if( ( trace->GetType() & SPT_AC_PHASE )
1168 || ( ( GetSimType() != ST_AC ) && ( trace->GetType() & SPT_CURRENT ) ) )
1169 {
1170 hasY2Traces = true;
1171 }
1172 else
1173 {
1174 hasY1Traces = true;
1175 }
1176 }
1177
1178 bool visibilityChanged = false;
1179
1180 if( m_axis_y1 && m_axis_y1->IsVisible() != hasY1Traces )
1181 {
1182 m_axis_y1->SetVisible( hasY1Traces );
1183 visibilityChanged = true;
1184 }
1185
1186 if( m_axis_y2 && m_axis_y2->IsVisible() != hasY2Traces )
1187 {
1188 m_axis_y2->SetVisible( hasY2Traces );
1189 visibilityChanged = true;
1190 }
1191
1192 if( m_axis_y3 && m_axis_y3->IsVisible() != hasY3Traces )
1193 {
1194 m_axis_y3->SetVisible( hasY3Traces );
1195 visibilityChanged = true;
1196 }
1197
1198 if( visibilityChanged )
1199 m_plotWin->UpdateAll();
1200}
1201
1202
1204{
1205 for( const auto& [ name, trace ] : m_traces )
1206 {
1207 if( trace == aTrace )
1208 {
1209 m_traces.erase( name );
1210 break;
1211 }
1212 }
1213
1214 for( const auto& [ id, cursor ] : aTrace->GetCursors() )
1215 {
1216 if( cursor )
1217 m_plotWin->DelLayer( cursor, true );
1218 }
1219
1220 m_plotWin->DelLayer( aTrace, true, true );
1221 ResetScales( false );
1223}
1224
1225
1226bool SIM_PLOT_TAB::DeleteTrace( const wxString& aVectorName, int aTraceType )
1227{
1228 if( TRACE* trace = GetTrace( aVectorName, aTraceType ) )
1229 {
1230 DeleteTrace( trace );
1231 return true;
1232 }
1233
1234 return false;
1235}
1236
1237
1238void SIM_PLOT_TAB::EnableCursor( TRACE* aTrace, int aCursorId, const wxString& aSignalName )
1239{
1240 CURSOR* cursor = new CURSOR( aTrace, this );
1241 mpWindow* win = GetPlotWin();
1242 int width = win->GetXScreen() - win->GetMarginLeft() - win->GetMarginRight();
1243 int center = win->GetMarginLeft() + KiROUND( width * ( aCursorId == 1 ? 0.4 : 0.6 ) );
1244
1245 cursor->SetName( aSignalName );
1246 cursor->SetX( center );
1247
1248 aTrace->SetCursor( aCursorId, cursor );
1249 m_plotWin->AddLayer( cursor );
1250
1251 // Notify the parent window about the changes
1252 wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1253}
1254
1255
1256void SIM_PLOT_TAB::DisableCursor( TRACE* aTrace, int aCursorId )
1257{
1258 if( CURSOR* cursor = aTrace->GetCursor( aCursorId ) )
1259 {
1260 aTrace->SetCursor( aCursorId, nullptr );
1261 GetPlotWin()->DelLayer( cursor, true );
1262
1263 // Notify the parent window about the changes
1264 wxQueueEvent( this, new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1265 }
1266}
1267
1268
1269void SIM_PLOT_TAB::ResetScales( bool aIncludeX )
1270{
1271 if( m_axis_x && aIncludeX )
1272 {
1273 m_axis_x->ResetDataRange();
1274
1275 if( GetSimType() == ST_TRAN )
1276 {
1277 wxStringTokenizer tokenizer( GetSimCommand(), " \t\r\n", wxTOKEN_STRTOK );
1278 wxString cmd = tokenizer.GetNextToken().Lower();
1279
1280 wxASSERT( cmd == wxS( ".tran" ) );
1281
1282 SPICE_VALUE step;
1283 SPICE_VALUE end( 1.0 );
1284 SPICE_VALUE start( 0.0 );
1285
1286 if( tokenizer.HasMoreTokens() )
1287 step = SPICE_VALUE( tokenizer.GetNextToken() );
1288
1289 if( tokenizer.HasMoreTokens() )
1290 end = SPICE_VALUE( tokenizer.GetNextToken() );
1291
1292 if( tokenizer.HasMoreTokens() )
1293 start = SPICE_VALUE( tokenizer.GetNextToken() );
1294
1295 static_cast<TIME_SCALE*>( m_axis_x )->SetStartAndEnd( start.ToDouble(), end.ToDouble() );
1296 }
1297 }
1298
1299 if( m_axis_y1 )
1300 m_axis_y1->ResetDataRange();
1301
1302 if( m_axis_y2 )
1303 m_axis_y2->ResetDataRange();
1304
1305 if( m_axis_y3 )
1306 m_axis_y3->ResetDataRange();
1307
1308 for( auto& [ name, trace ] : m_traces )
1309 trace->UpdateScales();
1310}
1311
1312
1313wxDEFINE_EVENT( EVT_SIM_CURSOR_UPDATE, wxCommandEvent );
const char * name
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
The SIMULATOR_FRAME holds the main user-interface for running simulations.
mpWindow * m_window
void Move(wxPoint aDelta) override
Moves the layer rectangle of given pixel deltas.
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)
int m_sweepIndex
bool OnDoubleClick(const wxPoint &aPoint, mpWindow &aWindow) override
void SetCoordX(double aValue)
void UpdateReference() override
Updates the rectangle reference point.
bool m_updateRequired
double m_snapTargetY
TRACE * m_trace
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot method.
bool m_snapToNearest
void Update()
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
COLOR4D & Invert()
Makes the color inverted, alpha remains the same.
Definition color4d.h:243
wxColour ToColour() const
Definition color4d.cpp:225
double Distance(const COLOR4D &other) const
Returns the distance (in RGB space) between two colors.
Definition color4d.cpp:553
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition color4d.h:296
void formatLabels() override
wxString GetUnits() const
const wxString m_unit
LIN_SCALE(const wxString &name, const wxString &unit, int flags)
wxString m_base_axis_label
wxString GetUnits() const
const wxString m_unit
LOG_SCALE(const wxString &name, const wxString &unit, int flags)
void formatLabels() override
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
mpScaleXBase * m_axis_x
mpWindow * GetPlotWin() const
void prepareDCAxes(int aNewTraceType)
Create/Ensure axes are available for plotting.
void SetTraceData(TRACE *aTrace, std::vector< double > &aX, std::vector< double > &aY, int aSweepCount, size_t aSweepSize, bool aIsMultiRun=false, const std::vector< wxString > &aMultiRunLabels={})
wxString GetUnitsY2() const
void SetY2Scale(bool aLock, double aMin, double aMax)
TRACE * GetTrace(const wxString &aVecName, int aType) const
virtual ~SIM_PLOT_TAB()
void SetY1Scale(bool aLock, double aMin, double aMax)
mpInfoLegend * m_legend
void UpdateAxisVisibility()
void SetY3Scale(bool aLock, double aMin, double aMax)
wxBoxSizer * m_sizer
std::map< wxString, TRACE * > m_traces
SIM_PLOT_COLORS m_colors
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void ResetScales(bool aIncludeX)
Update trace line style.
void UpdatePlotColors()
mpScaleY * m_axis_y2
SIM_PLOT_TAB(const wxString &aSimCommand, wxWindow *parent)
void EnableCursor(TRACE *aTrace, int aCursorId, const wxString &aSignalName)
wxString GetUnitsX() const
void OnLanguageChanged() override
Getter for math plot window.
void EnsureThirdYAxisExists()
wxString getTraceId(const wxString &aVectorName, int aType) const
Construct the plot axes for DC simulation plot.
TRACE * GetOrAddTrace(const wxString &aVectorName, int aType)
mpScaleY * m_axis_y1
mpScaleY * m_axis_y3
wxPoint m_LastLegendPosition
wxString GetUnitsY1() const
std::map< wxString, wxColour > m_sessionTraceColors
mpWindow * m_plotWin
void DisableCursor(TRACE *aTrace, int aCursorId)
Reset scale ranges to fit the current traces.
void updateAxes(int aNewTraceType=SIM_TRACE_TYPE::SPT_UNKNOWN)
wxString GetUnitsY3() const
SIM_TAB()
Definition sim_tab.cpp:33
SIM_TYPE GetSimType() const
Definition sim_tab.cpp:75
const wxString & GetSimCommand() const
Definition sim_tab.h:52
Helper class to recognize Spice formatted values.
Definition spice_value.h:56
double ToDouble() const
void ResetDataRange() override
void ExtendDataRange(double minV, double maxV) override
void SetStartAndEnd(double aStartTime, double aEndTime)
double m_startTime
TIME_SCALE(const wxString &name, const wxString &unit, int flags)
void SetIsMultiRun(bool aIsMultiRun)
void SetTraceColour(const wxColour &aColour)
void SetMultiRunLabels(const std::vector< wxString > &aLabels)
std::map< int, CURSOR * > & GetCursors()
SIM_TRACE_TYPE GetType() const
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)
wxColour GetTraceColour() const
CURSOR * GetCursor(int aCursorId)
void SetSweepSize(size_t aSweepSize)
Definition mathplot.h:1428
void SetSweepCount(int aSweepCount)
Definition mathplot.h:1427
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
wxPoint m_reference
Definition mathplot.h:383
wxRect m_dim
Definition mathplot.h:381
virtual void Move(wxPoint delta)
Moves the layer rectangle of given pixel deltas.
Definition mathplot.cpp:118
Implements the legend to be added to the plot This layer allows you to add a legend to describe the p...
Definition mathplot.h:395
const wxString & GetName() const
Get layer name.
Definition mathplot.h:239
bool m_continuous
Definition mathplot.h:312
bool m_visible
Definition mathplot.h:315
const wxPen & GetPen() const
Get pen set for this layer.
Definition mathplot.h:254
void SetPen(const wxPen &pen)
Set layer pen.
Definition mathplot.h:283
bool m_rangeSet
Definition mathplot.h:745
double m_maxV
Definition mathplot.h:744
double m_minV
Definition mathplot.h:744
Canvas for plotting mpLayer implementations.
Definition mathplot.h:910
int GetMarginLeft() const
Definition mathplot.h:1221
int GetScrX() const
Get current view's X dimension in device context units.
Definition mathplot.h:1031
int GetScrY() const
Get current view's Y dimension in device context units.
Definition mathplot.h:1040
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
Definition mathplot.h:1084
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition mathplot.h:1092
int GetXScreen() const
Definition mathplot.h:1032
int GetMarginTop() const
Definition mathplot.h:1215
bool DelLayer(mpLayer *layer, bool alsoDeleteObject=false, bool refreshDisplay=true)
Remove a plot layer from the canvas.
wxCoord y2p(double y)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
Definition mathplot.h:1096
int GetMarginRight() const
Definition mathplot.h:1217
int GetMarginBottom() const
Definition mathplot.h:1219
#define _(s)
#define mpALIGN_BORDER_RIGHT
Aligns Y axis to right border.
Definition mathplot.h:457
class WXDLLIMPEXP_MATHPLOT mpWindow
Definition mathplot.h:109
#define mpALIGN_RIGHT
Aligns label to the right.
Definition mathplot.h:429
#define mpALIGN_LEFT
Aligns label to the left.
Definition mathplot.h:433
#define mpALIGN_BOTTOM
Aligns label to the bottom.
Definition mathplot.h:437
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
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:50
@ SPT_AC_PHASE
Definition sim_types.h:54
@ SPT_AC_GAIN
Definition sim_types.h:55
@ SPT_VOLTAGE
Definition sim_types.h:52
@ SPT_POWER
Definition sim_types.h:56
@ SPT_CURRENT
Definition sim_types.h:53
@ ST_SP
Definition sim_types.h:43
@ ST_TRAN
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
@ ST_FFT
Definition sim_types.h:44
VECTOR2I center
VECTOR2I end
int delta
#define M_PI