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