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