24#include <wx/tokenzr.h>
40 fmt.Printf(
"%%.0%df", nDigits );
50static void getSISuffix(
double x,
const wxString& unit,
int& power, wxString& suffix )
52 const int n_powers = 11;
80 for(
int i = 0; i < n_powers - 1; i++ )
82 double r_cur = pow( 10, powers[i].exponent );
84 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
86 power = powers[i].exponent;
88 if( powers[i].suffix )
89 suffix = wxString( powers[i].suffix ) + unit;
101 if( std::isnan( x ) )
104 auto countSignificantDigits =
107 while( k && ( k % 10LL ) == 0LL )
121 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (
double) maxDigits ) );
122 int n = countSignificantDigits( k );
125 n = std::min( n, countSignificantDigits( k + 1 ) );
131template <
typename T_PARENT>
136 T_PARENT(
name, flags, false ),
145 double maxVis = T_PARENT::AbsVisibleMaxValue();
150 int constexpr MAX_DIGITS = 3;
151 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
152 bool duplicateLabels =
false;
156 double sf = pow( 10.0, power );
163 for(
size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
170 if( ii > 0 && l.
label == T_PARENT::m_tickLabels[ii-1].label )
171 duplicateLabels =
true;
174 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
199 LIN_SCALE::ExtendDataRange( minV, maxV );
229template <
typename T_PARENT>
234 T_PARENT(
name, flags, false ),
245 int constexpr MAX_DIGITS = 3;
250 double sf = pow( 10.0, power );
287 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
291 double plotY =
m_window->p2y( newY );
308 int sweepCount =
m_trace->GetSweepCount();
309 size_t sweepSize =
m_trace->GetSweepSize();
311 if( sweepCount <= 1 )
314 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
335 const std::vector<double>& dataX =
m_trace->GetDataX();
336 const std::vector<double>& dataY =
m_trace->GetDataY();
338 if( dataX.size() <= 1 )
346 size_t endIdx = dataX.size();
347 int sweepCount =
m_trace->GetSweepCount();
348 size_t sweepSize =
m_trace->GetSweepSize();
350 if( snapToNearest &&
m_trace->IsMultiRun() && sweepCount > 1
351 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0
352 && std::isfinite( snapTargetY ) )
354 double bestDistance = std::numeric_limits<double>::infinity();
358 for(
int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
360 size_t candidateStart =
static_cast<size_t>( sweepIdx ) * sweepSize;
361 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
363 if( candidateStart >= candidateEnd )
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;
372 if( candidateMinIdx < (
int) candidateStart
373 || candidateMaxIdx >= (
int) candidateEnd
374 || candidateMaxIdx >= (
int) dataX.size() )
379 double leftX = dataX[candidateMinIdx];
380 double rightX = dataX[candidateMaxIdx];
382 if( leftX == rightX )
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 );
393 bestSweep = sweepIdx;
402 if(
m_trace->IsMultiRun() && sweepCount > 1
403 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0 )
405 size_t available =
static_cast<size_t>( sweepCount ) * sweepSize;
407 if( available <= dataX.size() )
412 startIdx =
static_cast<size_t>(
m_sweepIndex ) * sweepSize;
413 endIdx = std::min( dataX.size(), startIdx + sweepSize );
425 if( startIdx >= endIdx )
431 auto beginIt = dataX.begin() + startIdx;
432 auto endIt = dataX.begin() + endIdx;
435 auto maxXIt = std::upper_bound( beginIt, endIt,
m_coords.x );
436 int maxIdx = maxXIt - dataX.begin();
437 int minIdx = maxIdx - 1;
440 if( minIdx < (
int) startIdx || maxIdx >= (
int) endIdx || maxIdx >= (
int) dataX.size() )
449 const double leftX = dataX[minIdx];
450 const double rightX = dataX[maxIdx];
451 const double leftY = dataY[minIdx];
452 const double rightY = dataY[maxIdx];
455 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
464 return wxString::Format(
_(
"%d" ),
id );
467 return wxEmptyString;
485 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
508 wxColour fg = aWindow.GetForegroundColour();
512 if( cursorColor.
Distance( textColor ) < 0.66 )
515 pen.SetColour( cursorColor.
ToColour() );
516 pen.SetStyle(
m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
519 if( topPx < cursorPos.y && cursorPos.y < bottomPx )
520 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
522 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
524 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
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 );
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 };
544 brush.SetStyle( wxBRUSHSTYLE_SOLID );
545 brush.SetColour(
m_trace->GetTraceColour() );
546 aDC.SetBrush( brush );
547 aDC.DrawPolygon( 3, poly );
549 aDC.SetTextForeground( textColor.
ToColour() );
550 aDC.DrawLabel(
id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
553 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
556 const std::vector<wxString>& labels =
m_trace->GetMultiRunLabels();
564 runLabel = wxString::Format(
_(
"Run %d" ),
m_sweepIndex + 1 );
567 wxSize runSize = aDC.GetTextExtent( runLabel );
568 int runX = textRect.GetRight() + 6;
569 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
571 runRect.Inflate( 3, 1 );
573 wxBrush labelBrush( aWindow.GetBackgroundColour() );
574 wxPen labelPen( cursorColor.
ToColour() );
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 );
591 return (
std::abs( (
double) aPoint.x -
609 SIM_TAB( aSimCommand, parent ),
616 m_sizer =
new wxBoxSizer( wxVERTICAL );
648 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
655 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
662 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
676 return wxEmptyString;
687 return wxEmptyString;
698 return wxEmptyString;
709 return wxEmptyString;
850 m_plotWin->SetMargins( 30, 160, 45, 70 );
888 if( sim_cmd.StartsWith(
".dc", &rem ) )
896 ch = rem.GetChar( 0 );
915 m_axis_x->SetName(
_(
"Voltage (swept)" ) );
926 m_axis_x->SetName(
_(
"Current (swept)" ) );
937 m_axis_x->SetName(
_(
"Resistance (swept)" ) );
948 m_axis_x->SetName(
_(
"Temperature (swept)" ) );
966 m_axis_y1->SetName(
_(
"Voltage (measured)" ) );
982 m_plotWin->SetMargins( 30, 160, 45, 70 );
1021 wxPenStyle penStyle;
1024 penStyle = wxPENSTYLE_SOLID;
1026 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1028 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1030 penStyle = wxPENSTYLE_SOLID;
1047 bool hasVoltageTraces =
false;
1049 for(
const auto& [
id, candidate ] :
m_traces )
1053 hasVoltageTraces =
true;
1058 if( !hasVoltageTraces )
1086 int aSweepCount,
size_t aSweepSize,
bool aIsMultiRun,
1087 const std::vector<wxString>& aMultiRunLabels )
1092 if( aX.size() > 0 && aX[0] == 0 )
1094 aX.erase( aX.begin() );
1095 aY.erase( aY.begin() );
1103 for(
double& pt : aY )
1104 pt = pt * 180.0 /
M_PI;
1108 for(
double& pt : aY )
1112 pt = 20 * log( pt ) / log( 10.0 );
1150 bool hasY1Traces =
false;
1151 bool hasY2Traces =
false;
1152 bool hasY3Traces =
false;
1174 bool visibilityChanged =
false;
1179 visibilityChanged =
true;
1185 visibilityChanged =
true;
1191 visibilityChanged =
true;
1194 if( visibilityChanged )
1203 if( trace == aTrace )
1216 m_plotWin->DelLayer( aTrace,
true,
true );
1241 cursor->SetName( aSignalName );
1248 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1256 aTrace->
SetCursor( aCursorId,
nullptr );
1260 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1273 wxStringTokenizer tokenizer(
GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
1274 wxString cmd = tokenizer.GetNextToken().Lower();
1276 wxASSERT( cmd == wxS(
".tran" ) );
1282 if( tokenizer.HasMoreTokens() )
1285 if( tokenizer.HasMoreTokens() )
1288 if( tokenizer.HasMoreTokens() )
1305 trace->UpdateScales();
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
The SIMULATOR_FRAME holds the main user-interface for running simulations.
void Move(wxPoint aDelta) override
Moves the layer rectangle of given pixel deltas.
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)
bool OnDoubleClick(const wxPoint &aPoint, mpWindow &aWindow) override
void SetCoordX(double aValue)
void UpdateReference() override
Updates the rectangle reference point.
void Plot(wxDC &aDC, mpWindow &aWindow) override
Plot method.
A color representation with 4 components: red, green, blue, alpha.
COLOR4D & Invert()
Makes the color inverted, alpha remains the same.
wxColour ToColour() const
double Distance(const COLOR4D &other) const
Returns the distance (in RGB space) between two colors.
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
void formatLabels() override
wxString GetUnits() const
LIN_SCALE(const wxString &name, const wxString &unit, int flags)
wxString m_base_axis_label
wxString GetUnits() const
LOG_SCALE(const wxString &name, const wxString &unit, int flags)
void formatLabels() override
bool DeleteTrace(const wxString &aVectorName, int aTraceType)
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
void SetY1Scale(bool aLock, double aMin, double aMax)
void UpdateAxisVisibility()
void SetY3Scale(bool aLock, double aMin, double aMax)
std::map< wxString, TRACE * > m_traces
void UpdateTraceStyle(TRACE *trace)
Update plot colors.
void ResetScales(bool aIncludeX)
Update trace line style.
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)
wxPoint m_LastLegendPosition
wxString GetUnitsY1() const
std::map< wxString, wxColour > m_sessionTraceColors
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_TYPE GetSimType() const
const wxString & GetSimCommand() const
Helper class to recognize Spice formatted values.
void ResetDataRange() override
void ExtendDataRange(double minV, double maxV) override
void SetStartAndEnd(double aStartTime, double aEndTime)
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)
void SetSweepCount(int aSweepCount)
virtual void SetScale(mpScaleBase *scaleX, mpScaleBase *scaleY)
virtual void Move(wxPoint delta)
Moves the layer rectangle of given pixel deltas.
Implements the legend to be added to the plot This layer allows you to add a legend to describe the p...
const wxString & GetName() const
Get layer name.
const wxPen & GetPen() const
Get pen set for this layer.
void SetPen(const wxPen &pen)
Set layer pen.
Canvas for plotting mpLayer implementations.
int GetMarginLeft() const
int GetScrX() const
Get current view's X dimension in device context units.
int GetScrY() const
Get current view's Y dimension in device context units.
double p2x(wxCoord pixelCoordX)
Converts mpWindow (screen) pixel coordinates into graph (floating point) coordinates,...
wxCoord x2p(double x)
Converts graph (floating point) coordinates into mpWindow (screen) pixel coordinates,...
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,...
int GetMarginRight() const
int GetMarginBottom() const
#define mpALIGN_BORDER_RIGHT
Aligns Y axis to right border.
class WXDLLIMPEXP_MATHPLOT mpWindow
#define mpALIGN_RIGHT
Aligns label to the right.
#define mpALIGN_LEFT
Aligns label to the left.
#define mpALIGN_BOTTOM
Aligns label to the bottom.
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
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)