Replaced sim plotting widget with wxMathPlot

This commit is contained in:
Maciej Suminski 2016-08-11 14:41:21 +02:00
parent 951d16c655
commit 51906625ee
8 changed files with 4802 additions and 211 deletions

View File

@ -169,6 +169,7 @@ set( COMMON_DLG_SRCS
)
set( COMMON_WIDGET_SRCS
widgets/mathplot.cpp
widgets/widget_hotkey_list.cpp
)

3001
common/widgets/mathplot.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -249,18 +249,12 @@ set_source_files_properties( ../common/single_top.cpp PROPERTIES
COMPILE_DEFINITIONS "TOP_FRAME=FRAME_SCH;PGM_DATA_FILE_EXT=\"sch\";BUILD_KIWAY_DLL"
)
#if (KICAD_SPICE)
set ( EESCHEMA_LINK_LIBS ${wxWidgets_LIBRARIES} mgl2 mgl2-wx)
#else()
# set ( EESCHEMA_LINK_LIBS ${wxWidgets_LIBRARIES} )
#endif()
target_link_libraries( eeschema
#singletop # replaces common, giving us restrictive control and link warnings.
# There's way too much crap coming in from common yet.
common
bitmaps
${EESCHEMA_LINK_LIBS}
${wxWidgets_LIBRARIES}
)
# the DSO (KIFACE) housing the main eeschema code:
@ -273,7 +267,7 @@ target_link_libraries( eeschema_kiface
bitmaps
polygon
gal
${EESCHEMA_LINK_LIBS}
${wxWidgets_LIBRARIES}
${GDI_PLUS_LIBRARIES}
)
set_target_properties( eeschema_kiface PROPERTIES

View File

@ -140,11 +140,15 @@ void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
if( nodeNumber >= -1 )
{
updatePlot( wxString::Format( "V(%d)", nodeNumber ), aNetName,
static_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetCurrentPage() ) );
updatePlot( wxString::Format( "V(%d)", nodeNumber ), aNetName, currentPlot() );
}
}
SIM_PLOT_PANEL* SIM_PLOT_FRAME::currentPlot() const
{
return static_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetCurrentPage() );
}
bool SIM_PLOT_FRAME::isSimulationRunning()
{
@ -184,7 +188,10 @@ void SIM_PLOT_FRAME::onSignalDblClick( wxCommandEvent& event )
int idx = m_signals->GetSelection();
if( idx != wxNOT_FOUND )
{
AddVoltagePlot( m_signals->GetString( idx ) );
currentPlot()->Fit();
}
}
@ -241,10 +248,9 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
for( unsigned int i = 0; i < m_plotNotebook->GetPageCount(); ++i )
{
SIM_PLOT_PANEL* plotPanel = static_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( i ) );
plotPanel->ResetAxisRanges();
for( const auto& trace : plotPanel->GetTraces() )
updatePlot( trace.spiceName, trace.title, plotPanel );
updatePlot( trace->GetSpiceName(), trace->GetName(), plotPanel );
}
}

View File

@ -59,6 +59,8 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE
void AddVoltagePlot( const wxString& aNetName );
private:
SIM_PLOT_PANEL* currentPlot() const;
bool isSimulationRunning();
/**

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2016 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,188 +30,93 @@
SIM_PLOT_PANEL::SIM_PLOT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style, const wxString& name )
: wxMathGL( parent, id, pos, size, style, name ), m_painter( this )
: mpWindow( parent, id, pos, size, style ), m_colorIdx( 0 )
{
AutoResize = true;
ResetAxisRanges();
SetDraw( &m_painter );
//SetMargins( 10, 10, 10, 10 );
LockAspect();
m_axis_x = new mpScaleX( wxT( "T [s]" ) );
m_axis_x->SetTicks( false );
AddLayer( m_axis_x );
m_axis_y = new mpScaleY( wxT( "U [V]" ) );
m_axis_y->SetTicks( false );
AddLayer( m_axis_y );
m_legend = new mpInfoLegend( wxRect( 0, 0, 40, 40 ), wxWHITE_BRUSH );
AddLayer( m_legend );
//m_coords = new mpInfoCoords( wxRect( 80, 20, 10, 10 ), wxWHITE_BRUSH );
//AddLayer( m_coords );
}
SIM_PLOT_PANEL::~SIM_PLOT_PANEL()
{
}
template<typename T>
static std::pair<T, T> find_minmax( const T* aArray, unsigned int aSize )
{
std::pair<T, T> result( std::numeric_limits<T>::max(), std::numeric_limits<T>::min() );
const T* ptr = aArray;
for( unsigned int i = 0; i < aSize; ++i )
{
if( *ptr < result.first )
result.first = *ptr;
if( *ptr > result.second )
result.second = *ptr;
++ptr;
}
return result;
// ~mpWindow destroys all the added layers, so there is no need to destroy m_traces contents
}
void SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aTitle, int aPoints,
double* aT, double* aY, int aFlags )
const double* aT, const double* aY, int aFlags )
{
TRACE* t = NULL;
// Find previous entry, if there is one
auto it = std::find_if( m_traces.begin(), m_traces.end(),
[&](const TRACE& t) { return t.title == aTitle; });
[&](const TRACE* t) { return t->GetName() == aTitle; });
if( it == m_traces.end() )
{
// New entry
TRACE trace;
trace.spiceName = aSpiceName;
trace.title = aTitle;
trace.style = wxString( '-' ) + m_painter.GenerateColor( SIM_PLOT_PAINTER::DARK );
trace.x.Set( aT, aPoints );
trace.y.Set( aY, aPoints );
m_traces.push_back( trace );
t = new TRACE( aTitle, aSpiceName );
t->SetPen( wxPen( generateColor(), 1, wxSOLID ) );
m_traces.push_back( t );
// It is a trick to keep legend always on the top
DelLayer( m_legend );
AddLayer( t );
AddLayer( m_legend );
}
else
{
// Update
TRACE& trace = *it;
trace.x.Set( aT, aPoints );
trace.y.Set( aY, aPoints );
t = *it;
}
// Update axis ranges
std::pair<double, double> traceRangeT = find_minmax( aT, aPoints );
m_axisRangeX.first = std::min( traceRangeT.first, m_axisRangeX.first );
m_axisRangeX.second = std::max( traceRangeT.second, m_axisRangeX.second );
std::pair<double, double> traceRangeY = find_minmax( aY, aPoints );
m_axisRangeY.first = std::min( traceRangeY.first, m_axisRangeY.first );
m_axisRangeY.second = std::max( traceRangeY.second, m_axisRangeY.second );
Update();
t->SetData( std::vector<double>( aT, aT + aPoints ), std::vector<double>( aY, aY + aPoints ) );
UpdateAll();
}
void SIM_PLOT_PANEL::DeleteTraces()
{
for( TRACE* t : m_traces )
{
DelLayer( t, true );
}
m_traces.clear();
ResetAxisRanges();
Update();
}
void SIM_PLOT_PANEL::ResetAxisRanges()
wxColour SIM_PLOT_PANEL::generateColor()
{
// Set ranges to inverted values, so when there is a new plot added, it will
// overridden with correct values
m_axisRangeX.first = std::numeric_limits<double>::max();
m_axisRangeX.second = std::numeric_limits<double>::min();
m_axisRangeY.first = std::numeric_limits<double>::max();
m_axisRangeY.second = std::numeric_limits<double>::min();
}
int SIM_PLOT_PAINTER::Draw( mglGraph* aGraph )
{
const std::vector<SIM_PLOT_PANEL::TRACE>& traces = m_parent->m_traces;
const std::pair<double, double>& axisRangeX = m_parent->m_axisRangeX;
const std::pair<double, double>& axisRangeY = m_parent->m_axisRangeY;
aGraph->Clf();
//aGraph->SetPlotFactor( 1.5 );
//aGraph->LoadFont( "termes" );
aGraph->SetFontSize( 1.5 );
// Axis settings
// Use autorange values if possible
if( axisRangeX.first < axisRangeX.second )
aGraph->SetRange( 'x', axisRangeX.first, axisRangeX.second );
else
aGraph->SetRange( 'x', 0, 1 );
if( axisRangeY.first < axisRangeY.second )
{
// Increase the Y axis range, so it is easy to read the extreme values
double range = axisRangeY.second - axisRangeY.first;
aGraph->SetRange( 'y', axisRangeY.first - 0.1 * range, axisRangeY.second + 0.1 * range );
}
else
{
aGraph->SetRange( 'y', 0, 1 );
}
aGraph->Axis( "xy" );
aGraph->Label( 'x', "Time [s]", 0 );
aGraph->Label( 'y', "Voltage [V]", 0 );
aGraph->Box();
aGraph->Grid();
// Draw traces
for( auto t : traces )
{
aGraph->AddLegend( (const char*) t.title.c_str(), t.style );
aGraph->Plot( t.y, t.style );
}
if( traces.size() )
{
aGraph->SetFontSize( 2.5 );
aGraph->Legend( 1, "-#" ); // legend entries horizontally + draw a box around legend
}
return 0;
}
wxString SIM_PLOT_PAINTER::GenerateColor( COLOR_TYPE aType )
{
const char colors[] = "rgbcmylenupq";
const unsigned int colorsNumber = sizeof( colors ) - 1;
// Safe defaults
char color = 'k'; // black
int shade = 5;
switch( aType )
{
case LIGHT:
color = colors[m_lightColorIdx % colorsNumber];
shade = 5 + m_lightColorIdx / colorsNumber;
++m_lightColorIdx;
if( shade == 10 )
{
// Reached the color limit
shade = 5;
m_lightColorIdx = 0;
}
break;
case DARK:
color = toupper( colors[m_darkColorIdx % colorsNumber] );
shade = 5 - m_darkColorIdx / colorsNumber;
++m_darkColorIdx;
if( shade == 0 )
{
// Reached the color limit
shade = 5;
m_darkColorIdx = 0;
}
break;
}
return wxString::Format( "{%c%d}", color, shade );
/// @todo have a look at:
/// http://stanford.edu/~mwaskom/software/seaborn/tutorial/color_palettes.html
/// https://github.com/Gnuplotting/gnuplot-palettes
const unsigned long colors[] = { 0x000080, 0x008000, 0x800000, 0x008080, 0x800080, 0x808000, 0x808080 };
//const unsigned long colors[] = { 0xe3cea6, 0xb4781f, 0x8adfb2, 0x2ca033, 0x999afb, 0x1c1ae3, 0x6fbffd, 0x007fff, 0xd6b2ca, 0x9a3d6a };
// hls
//const unsigned long colors[] = { 0x0f1689, 0x0f7289, 0x35890f, 0x0f8945, 0x89260f, 0x890f53, 0x89820f, 0x630f89 };
// pastels, good for dark background
//const unsigned long colors[] = { 0x2fd8fe, 0x628dfa, 0x53d8a6, 0xa5c266, 0xb3b3b3, 0x94c3e4, 0xca9f8d, 0xac680e };
const unsigned int colorCount = sizeof(colors) / sizeof(unsigned long);
/// @todo generate shades to avoid repeating colors
return wxColour( colors[m_colorIdx++ % colorCount] );
}

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2016 CERN
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,38 +26,9 @@
#ifndef __SIM_PLOT_PANEL_H
#define __SIM_PLOT_PANEL_H
#include "mgl2/wx.h"
#include <widgets/mathplot.h>
class SIM_PLOT_PANEL;
class SIM_PLOT_PAINTER : public mglDraw
{
public:
SIM_PLOT_PAINTER( const SIM_PLOT_PANEL* aParent )
: m_parent( aParent ), m_lightColorIdx( 0 ), m_darkColorIdx( 0 )
{
}
~SIM_PLOT_PAINTER()
{
}
//void Click() override;
int Draw( mglGraph* aGraph ) override;
enum COLOR_TYPE { LIGHT, DARK };
///> Generates a new, unique color for plotting curves
wxString GenerateColor( COLOR_TYPE aType );
private:
const SIM_PLOT_PANEL* m_parent;
int m_lightColorIdx, m_darkColorIdx;
};
class SIM_PLOT_PANEL : public wxMathGL
class SIM_PLOT_PANEL : public mpWindow
{
public:
SIM_PLOT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
@ -64,33 +36,47 @@ public:
~SIM_PLOT_PANEL();
struct TRACE
{
wxString spiceName, title, style;
mglData x, y;
};
void AddTrace( const wxString& aSpiceName, const wxString& aTitle, int aPoints,
double* aT, double* aY, int aFlags );
const double* aT, const double* aY, int aFlags );
void DeleteTraces();
const std::vector<TRACE>& GetTraces() const
class TRACE : public mpFXYVector
{
public:
TRACE( const wxString& aTitle, const wxString& aSpiceName )
: mpFXYVector( aTitle ), m_spiceName( aSpiceName )
{
SetContinuity( true );
ShowName( false );
}
const wxString& GetSpiceName() const
{
return m_spiceName;
}
private:
wxString m_spiceName;
};
const std::vector<TRACE*>& GetTraces() const
{
return m_traces;
}
void ResetAxisRanges();
private:
SIM_PLOT_PAINTER m_painter;
wxColour generateColor();
unsigned int m_colorIdx;
// Traces to be plotted
std::vector<TRACE> m_traces;
std::vector<TRACE*> m_traces;
// Axis ranges determined by the added traces data
std::pair<double, double> m_axisRangeX, m_axisRangeY;
friend class SIM_PLOT_PAINTER;
mpScaleX* m_axis_x;
mpScaleY* m_axis_y;
mpInfoLegend* m_legend;
//mpInfoCoords* m_coords;
};
#endif

1695
include/widgets/mathplot.h Normal file

File diff suppressed because it is too large Load Diff