28#include <wx/tokenzr.h>
44 fmt.Printf(
"%%.0%df", nDigits );
54static void getSISuffix(
double x,
const wxString& unit,
int& power, wxString& suffix )
56 const int n_powers = 11;
84 for(
int i = 0; i < n_powers - 1; i++ )
86 double r_cur = pow( 10, powers[i].exponent );
88 if( fabs( x ) >= r_cur && fabs( x ) < r_cur * 1000.0 )
90 power = powers[i].exponent;
92 if( powers[i].suffix )
93 suffix = wxString( powers[i].suffix ) + unit;
105 if( std::isnan( x ) )
108 auto countSignificantDigits =
111 while( k && ( k % 10LL ) == 0LL )
125 int64_t k = (int)( ( x - floor( x ) ) * pow( 10.0, (
double) maxDigits ) );
126 int n = countSignificantDigits( k );
129 n = std::min( n, countSignificantDigits( k + 1 ) );
135template <
typename T_PARENT>
140 T_PARENT(
name, flags, false ),
149 double maxVis = T_PARENT::AbsVisibleMaxValue();
154 int constexpr MAX_DIGITS = 3;
155 int constexpr MAX_DISAMBIGUATION_DIGITS = 6;
156 bool duplicateLabels =
false;
160 double sf = pow( 10.0, power );
167 for(
size_t ii = 0; ii < T_PARENT::m_tickLabels.size(); ++ii )
174 if( ii > 0 && l.
label == T_PARENT::m_tickLabels[ii-1].label )
175 duplicateLabels =
true;
178 while( duplicateLabels && ++digits <= MAX_DISAMBIGUATION_DIGITS );
203 LIN_SCALE::ExtendDataRange( minV, maxV );
233template <
typename T_PARENT>
238 T_PARENT(
name, flags, false ),
249 int constexpr MAX_DIGITS = 3;
254 double sf = pow( 10.0, power );
291 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
295 double plotY =
m_window->p2y( newY );
312 int sweepCount =
m_trace->GetSweepCount();
313 size_t sweepSize =
m_trace->GetSweepSize();
315 if( sweepCount <= 1 )
318 if( sweepSize == std::numeric_limits<size_t>::max() || sweepSize == 0 )
339 const std::vector<double>& dataX =
m_trace->GetDataX();
340 const std::vector<double>& dataY =
m_trace->GetDataY();
342 if( dataX.size() <= 1 )
350 size_t endIdx = dataX.size();
351 int sweepCount =
m_trace->GetSweepCount();
352 size_t sweepSize =
m_trace->GetSweepSize();
354 if( snapToNearest &&
m_trace->IsMultiRun() && sweepCount > 1
355 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0
356 && std::isfinite( snapTargetY ) )
358 double bestDistance = std::numeric_limits<double>::infinity();
362 for(
int sweepIdx = 0; sweepIdx < sweepCount; ++sweepIdx )
364 size_t candidateStart =
static_cast<size_t>( sweepIdx ) * sweepSize;
365 size_t candidateEnd = std::min( dataX.size(), candidateStart + sweepSize );
367 if( candidateStart >= candidateEnd )
370 auto candidateBegin = dataX.begin() + candidateStart;
371 auto candidateEndIt = dataX.begin() + candidateEnd;
372 auto candidateMaxIt = std::upper_bound( candidateBegin, candidateEndIt,
m_coords.x );
373 int candidateMaxIdx = candidateMaxIt - dataX.begin();
374 int candidateMinIdx = candidateMaxIdx - 1;
376 if( candidateMinIdx < (
int) candidateStart
377 || candidateMaxIdx >= (
int) candidateEnd
378 || candidateMaxIdx >= (
int) dataX.size() )
383 double leftX = dataX[candidateMinIdx];
384 double rightX = dataX[candidateMaxIdx];
386 if( leftX == rightX )
389 double leftY = dataY[candidateMinIdx];
390 double rightY = dataY[candidateMaxIdx];
391 double value = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
392 double distance = std::fabs( value - snapTargetY );
397 bestSweep = sweepIdx;
406 if(
m_trace->IsMultiRun() && sweepCount > 1
407 && sweepSize != std::numeric_limits<size_t>::max() && sweepSize > 0 )
409 size_t available =
static_cast<size_t>( sweepCount ) * sweepSize;
411 if( available <= dataX.size() )
416 startIdx =
static_cast<size_t>(
m_sweepIndex ) * sweepSize;
417 endIdx = std::min( dataX.size(), startIdx + sweepSize );
429 if( startIdx >= endIdx )
435 auto beginIt = dataX.begin() + startIdx;
436 auto endIt = dataX.begin() + endIdx;
439 auto maxXIt = std::upper_bound( beginIt, endIt,
m_coords.x );
440 int maxIdx = maxXIt - dataX.begin();
441 int minIdx = maxIdx - 1;
444 if( minIdx < (
int) startIdx || maxIdx >= (
int) endIdx || maxIdx >= (
int) dataX.size() )
453 const double leftX = dataX[minIdx];
454 const double rightX = dataX[maxIdx];
455 const double leftY = dataY[minIdx];
456 const double rightY = dataY[maxIdx];
459 m_coords.y = leftY + ( rightY - leftY ) / ( rightX - leftX ) * (
m_coords.x - leftX );
468 return wxString::Format(
_(
"%d" ),
id );
471 return wxEmptyString;
489 wxQueueEvent( aWindow.GetParent(),
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
512 wxColour fg = aWindow.GetForegroundColour();
516 if( cursorColor.
Distance( textColor ) < 0.66 )
519 pen.SetColour( cursorColor.
ToColour() );
520 pen.SetStyle(
m_continuous ? wxPENSTYLE_SOLID : wxPENSTYLE_LONG_DASH );
523 if( topPx < cursorPos.y && cursorPos.y < bottomPx )
524 aDC.DrawLine( leftPx, cursorPos.y, rightPx, cursorPos.y );
526 if( leftPx < cursorPos.x && cursorPos.x < rightPx )
528 aDC.DrawLine( cursorPos.x, topPx, cursorPos.x, bottomPx );
530 wxString
id =
getID();
531 wxSize size = aDC.GetTextExtent( wxS(
"M" ) );
532 wxRect textRect( wxPoint( cursorPos.x + 1 - size.x / 2, topPx - 4 - size.y ), size );
543 size.y = ( size.y / 2 ) * 2;
544 poly[0] = { cursorPos.x - 1 - size.y / 2, topPx - size.y };
545 poly[1] = { cursorPos.x + 1 + size.y / 2, topPx - size.y };
546 poly[2] = { cursorPos.x, topPx };
548 brush.SetStyle( wxBRUSHSTYLE_SOLID );
549 brush.SetColour(
m_trace->GetTraceColour() );
550 aDC.SetBrush( brush );
551 aDC.DrawPolygon( 3, poly );
553 aDC.SetTextForeground( textColor.
ToColour() );
554 aDC.DrawLabel(
id, textRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
557 &&
m_trace->GetSweepSize() != std::numeric_limits<size_t>::max() )
560 const std::vector<wxString>& labels =
m_trace->GetMultiRunLabels();
568 runLabel = wxString::Format(
_(
"Run %d" ),
m_sweepIndex + 1 );
571 wxSize runSize = aDC.GetTextExtent( runLabel );
572 int runX = textRect.GetRight() + 6;
573 wxRect runRect( wxPoint( runX, textRect.y ), runSize );
575 runRect.Inflate( 3, 1 );
577 wxBrush labelBrush( aWindow.GetBackgroundColour() );
578 wxPen labelPen( cursorColor.
ToColour() );
580 aDC.SetPen( labelPen );
581 aDC.SetBrush( labelBrush );
582 aDC.DrawRectangle( runRect );
583 aDC.SetTextForeground( cursorColor.
ToColour() );
584 aDC.DrawLabel( runLabel, runRect, wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL );
595 return (
std::abs( (
double) aPoint.x -
613 SIM_TAB( aSimCommand, parent ),
620 m_sizer =
new wxBoxSizer( wxVERTICAL );
652 m_axis_y1->SetAxisMinMax( aLock, aMin, aMax );
659 m_axis_y2->SetAxisMinMax( aLock, aMin, aMax );
666 m_axis_y3->SetAxisMinMax( aLock, aMin, aMax );
680 return wxEmptyString;
691 return wxEmptyString;
702 return wxEmptyString;
713 return wxEmptyString;
854 m_plotWin->SetMargins( 30, 160, 45, 70 );
892 if( sim_cmd.StartsWith(
".dc", &rem ) )
900 ch = rem.GetChar( 0 );
919 m_axis_x->SetName(
_(
"Voltage (swept)" ) );
930 m_axis_x->SetName(
_(
"Current (swept)" ) );
941 m_axis_x->SetName(
_(
"Resistance (swept)" ) );
952 m_axis_x->SetName(
_(
"Temperature (swept)" ) );
970 m_axis_y1->SetName(
_(
"Voltage (measured)" ) );
986 m_plotWin->SetMargins( 30, 160, 45, 70 );
1025 wxPenStyle penStyle;
1028 penStyle = wxPENSTYLE_SOLID;
1030 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1032 penStyle =
m_dotted_cp ? wxPENSTYLE_DOT : wxPENSTYLE_SOLID;
1034 penStyle = wxPENSTYLE_SOLID;
1051 bool hasVoltageTraces =
false;
1053 for(
const auto& [
id, candidate ] :
m_traces )
1057 hasVoltageTraces =
true;
1062 if( !hasVoltageTraces )
1090 int aSweepCount,
size_t aSweepSize,
bool aIsMultiRun,
1091 const std::vector<wxString>& aMultiRunLabels )
1096 if( aX.size() > 0 && aX[0] == 0 )
1098 aX.erase( aX.begin() );
1099 aY.erase( aY.begin() );
1107 for(
double& pt : aY )
1108 pt = pt * 180.0 /
M_PI;
1112 for(
double& pt : aY )
1116 pt = 20 * log( pt ) / log( 10.0 );
1154 bool hasY1Traces =
false;
1155 bool hasY2Traces =
false;
1156 bool hasY3Traces =
false;
1178 bool visibilityChanged =
false;
1183 visibilityChanged =
true;
1189 visibilityChanged =
true;
1195 visibilityChanged =
true;
1198 if( visibilityChanged )
1207 if( trace == aTrace )
1220 m_plotWin->DelLayer( aTrace,
true,
true );
1245 cursor->SetName( aSignalName );
1252 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1260 aTrace->
SetCursor( aCursorId,
nullptr );
1264 wxQueueEvent(
this,
new wxCommandEvent( EVT_SIM_CURSOR_UPDATE ) );
1277 wxStringTokenizer tokenizer(
GetSimCommand(),
" \t\r\n", wxTOKEN_STRTOK );
1278 wxString cmd = tokenizer.GetNextToken().Lower();
1280 wxASSERT( cmd == wxS(
".tran" ) );
1286 if( tokenizer.HasMoreTokens() )
1289 if( tokenizer.HasMoreTokens() )
1292 if( tokenizer.HasMoreTokens() )
1309 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)