43 fmt.Printf(
"%%.0%df", nDigits );
53static void getSISuffix(
double x,
const wxString& unit,
int& power, wxString& suffix )
55 const int n_powers = 11;
83 for(
int i = 0; i < n_powers - 1; i++ )
85 double r_cur = pow( 10, powers[i].exponent );
87 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
89 power = powers[i].exponent;
91 if( powers[i].suffix )
92 suffix = wxString( powers[i].suffix ) + unit;
104 if( std::isnan( x ) )
107 auto countSignificantDigits =
110 while( k && ( k % 10LL ) == 0LL )
124 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (
double) maxDigits ) );
125 int n = countSignificantDigits( k );
128 n = std::min( n, countSignificantDigits( k + 1 ) );
134template <
typename T_PARENT>
139 T_PARENT(
name, flags, false ),
148 double maxVis = T_PARENT::AbsVisibleMaxValue();
153 int constexpr MAX_DIGITS = 3;
154 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
155 bool duplicateLabels =
false;
159 double sf = pow( 10.0, power );
166 for(
size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
173 if( ii > 0 && l.
label == T_PARENT::m_tickLabels[ii-1].label )
174 duplicateLabels =
true;
177 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
202 LIN_SCALE::ExtendDataRange( minV, maxV );
232template <
typename T_PARENT>
237 T_PARENT(
name, flags, false ),
248 int constexpr MAX_DIGITS = 3;
253 double sf = pow( 10.0, power );
290 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
294 double plotY =
m_window->p2y( newY );
311 int sweepCount =
m_trace->GetSweepCount();
312 size_t sweepSize =
m_trace->GetSweepSize();
314 if( sweepCount <= 1 )
317 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
338 const std::vector<double>& dataX =
m_trace->GetDataX();
339 const std::vector<double>& dataY =
m_trace->GetDataY();
341 if( dataX.size() <= 1 )
349 size_t endIdx = dataX.size();
350 int sweepCount =
m_trace->GetSweepCount();
351 size_t sweepSize =
m_trace->GetSweepSize();
353 if( snapToNearest &&
m_trace->IsMultiRun() && sweepCount > 1
354 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0
355 && std::isfinite( snapTargetY ) )
357 double bestDistance = std::numeric_limits<double>::infinity();
361 for(
int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
363 size_t candidateStart =
static_cast<size_t>( sweepIdx ) * sweepSize;
364 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
366 if( candidateStart >= candidateEnd )
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;
375 if( candidateMinIdx < (
int) candidateStart
376 || candidateMaxIdx >= (
int) candidateEnd
377 || candidateMaxIdx >= (
int) dataX.size() )
382 double leftX = dataX[candidateMinIdx];
383 double rightX = dataX[candidateMaxIdx];
385 if( leftX == rightX )
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 );
396 bestSweep = sweepIdx;
405 if(
m_trace->IsMultiRun() && sweepCount > 1
406 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0 )
408 size_t available =
static_cast<size_t>( sweepCount ) * sweepSize;
410 if( available <= dataX.size() )
415 startIdx =
static_cast<size_t>(
m_sweepIndex ) * sweepSize;
416 endIdx = std::min( dataX.size(), startIdx + sweepSize );
428 if( startIdx >= endIdx )
434 auto beginIt = dataX.begin() + startIdx;
435 auto endIt = dataX.begin() + endIdx;
438 auto maxXIt = std::upper_bound( beginIt, endIt,
m_coords.x );
439 int maxIdx = maxXIt - dataX.begin();
440 int minIdx = maxIdx - 1;
443 if( minIdx < (
int) startIdx || maxIdx >= (
int) endIdx || maxIdx >= (
int) dataX.size() )
452 const double leftX = dataX[minIdx];
453 const double rightX = dataX[maxIdx];
454 const double leftY = dataY[minIdx];
455 const double rightY = dataY[maxIdx];
458 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
467 return wxString::Format(
_(
"%d" ),
id );
470 return wxEmptyString;
488 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
511 wxColour fg = aWindow.GetForegroundColour();
515 if( cursorColor.
Distance( textColor ) < 0.66 )
518 pen.SetColour( cursorColor.
ToColour() );
519 pen.SetStyle(
m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
522 if( topPx < cursorPos.y && cursorPos.y < bottomPx )
523 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
525 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
527 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
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 );
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 };
547 brush.SetStyle( wxBRUSHSTYLE_SOLID );
548 brush.SetColour(
m_trace->GetTraceColour() );
549 aDC.SetBrush( brush );
550 aDC.DrawPolygon( 3, poly );
552 aDC.SetTextForeground( textColor.
ToColour() );
553 aDC.DrawLabel(
id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
556 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
559 const std::vector<wxString>& labels =
m_trace->GetMultiRunLabels();
567 runLabel = wxString::Format(
_(
"Run %d" ),
m_sweepIndex + 1 );
570 wxSize runSize = aDC.GetTextExtent( runLabel );
571 int runX = textRect.GetRight() + 6;
572 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
574 runRect.Inflate( 3, 1 );
576 wxBrush labelBrush( aWindow.GetBackgroundColour() );
577 wxPen labelPen( cursorColor.
ToColour() );
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 );
594 return (
std::abs( (
double) aPoint.x -
612 SIM_TAB( aSimCommand, parent ),
619 m_sizer =
new wxBoxSizer( wxVERTICAL );
651 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
658 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
665 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
679 return wxEmptyString;
690 return wxEmptyString;
701 return wxEmptyString;
712 return wxEmptyString;
853 m_plotWin->SetMargins( 30, 160, 45, 70 );
891 if( sim_cmd.StartsWith(
".dc", &rem ) )
899 ch = rem.GetChar( 0 );
918 m_axis_x->SetName(
_(
"Voltage (swept)" ) );
929 m_axis_x->SetName(
_(
"Current (swept)" ) );
940 m_axis_x->SetName(
_(
"Resistance (swept)" ) );
951 m_axis_x->SetName(
_(
"Temperature (swept)" ) );
969 m_axis_y1->SetName(
_(
"Voltage (measured)" ) );
985 m_plotWin->SetMargins( 30, 160, 45, 70 );
1024 wxPenStyle penStyle;
1027 penStyle = wxPENSTYLE_SOLID;
1029 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1031 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1033 penStyle = wxPENSTYLE_SOLID;
1050 bool hasVoltageTraces =
false;
1052 for(
const auto& [
id, candidate ] :
m_traces )
1056 hasVoltageTraces =
true;
1061 if( !hasVoltageTraces )
1089 int aSweepCount,
size_t aSweepSize,
bool aIsMultiRun,
1090 const std::vector<wxString>& aMultiRunLabels )
1095 if( aX.size() > 0 && aX[0] == 0 )
1097 aX.erase( aX.begin() );
1098 aY.erase( aY.begin() );
1106 for(
double& pt : aY )
1107 pt = pt * 180.0 /
M_PI;
1111 for(
double& pt : aY )
1115 pt = 20 * log( pt ) / log( 10.0 );
1153 bool hasY1Traces =
false;
1154 bool hasY2Traces =
false;
1155 bool hasY3Traces =
false;
1177 bool visibilityChanged =
false;
1182 visibilityChanged =
true;
1188 visibilityChanged =
true;
1194 visibilityChanged =
true;
1197 if( visibilityChanged )
1206 if( trace == aTrace )
1219 m_plotWin->DelLayer( aTrace,
true,
true );
1244 cursor->SetName( aSignalName );
1251 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1259 aTrace->
SetCursor( aCursorId,
nullptr );
1263 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1276 wxStringTokenizer tokenizer(
GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
1277 wxString cmd = tokenizer.GetNextToken().Lower();
1279 wxASSERT( cmd == wxS(
".tran" ) );
1285 if( tokenizer.HasMoreTokens() )
1288 if( tokenizer.HasMoreTokens() )
1291 if( tokenizer.HasMoreTokens() )
1308 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)