diff options
author | Thales Lima Oliveira <thaleslima.ufu@gmail.com> | 2017-04-24 17:37:23 -0300 |
---|---|---|
committer | Thales Lima Oliveira <thaleslima.ufu@gmail.com> | 2017-04-24 17:37:23 -0300 |
commit | 9529a6ed44645842adc6f938478acc1dfa17a284 (patch) | |
tree | 725e524253d6fd714460402194b408cb33b80b3f /Project/wxMathPlot/mathplot.cpp | |
parent | 341b4a77d2b4c69a99370065af166d92071c9207 (diff) | |
download | PSP.git-9529a6ed44645842adc6f938478acc1dfa17a284.tar.gz PSP.git-9529a6ed44645842adc6f938478acc1dfa17a284.tar.xz PSP.git-9529a6ed44645842adc6f938478acc1dfa17a284.zip |
Chart view implementation start... New branch
Diffstat (limited to 'Project/wxMathPlot/mathplot.cpp')
-rw-r--r-- | Project/wxMathPlot/mathplot.cpp | 3074 |
1 files changed, 3074 insertions, 0 deletions
diff --git a/Project/wxMathPlot/mathplot.cpp b/Project/wxMathPlot/mathplot.cpp new file mode 100644 index 0000000..8543ca7 --- /dev/null +++ b/Project/wxMathPlot/mathplot.cpp @@ -0,0 +1,3074 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: mathplot.cpp +// Purpose: Framework for plotting in wxWindows +// Original Author: David Schalig +// Maintainer: Davide Rondini +// Contributors: Jose Luis Blanco, Val Greene +// Created: 21/07/2003 +// Last edit: 09/09/2007 +// Copyright: (c) David Schalig, Davide Rondini +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ +// #pragma implementation "plot.h" +#pragma implementation "mathplot.h" +#endif + +// For compilers that support precompilation, includes "wx.h". +#include <wx/window.h> +//#include <wx/wxprec.h> + +// Comment out for release operation: +// (Added by J.L.Blanco, Aug 2007) +// #define MATHPLOT_DO_LOGGING + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include "wx/object.h" +#include "wx/font.h" +#include "wx/colour.h" +#include "wx/settings.h" +#include "wx/sizer.h" +//#include "wx/log.h" +#include "wx/intl.h" +#include "wx/dcclient.h" +#include "wx/cursor.h" +#endif + +#include "mathplot.h" +#include <wx/bmpbuttn.h> +#include <wx/module.h> +#include <wx/msgdlg.h> +#include <wx/image.h> +#include <wx/tipwin.h> + +#include <cmath> +#include <cstdio> // used only for debug +#include <ctime> // used for representation of x axes involving date + +// #include "pixel.xpm" + +// Memory leak debugging +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + +// Legend margins +#define mpLEGEND_MARGIN 5 +#define mpLEGEND_LINEWIDTH 40 + +// Minimum axis label separation +#define mpMIN_X_AXIS_LABEL_SEPARATION 64 +#define mpMIN_Y_AXIS_LABEL_SEPARATION 32 + +// Number of pixels to scroll when scrolling by a line +#define mpSCROLL_NUM_PIXELS_PER_LINE 10 + +// See doxygen comments. +double mpWindow::zoomIncrementalFactor = 1.5; + +//----------------------------------------------------------------------------- +// mpLayer +//----------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(mpLayer, wxObject) + +mpLayer::mpLayer() : m_type(mpLAYER_UNDEF) +{ + SetPen((wxPen&) *wxBLACK_PEN); + SetFont((wxFont&) *wxNORMAL_FONT); + m_continuous = FALSE; // Default + m_showName = TRUE; // Default + m_drawOutsideMargins = TRUE; + m_visible = true; +} + +wxBitmap mpLayer::GetColourSquare(int side) +{ + wxBitmap square(side, side, -1); + wxColour filler = m_pen.GetColour(); + wxBrush brush(filler, wxSOLID); + wxMemoryDC dc; + dc.SelectObject(square); + dc.SetBackground(brush); + dc.Clear(); + dc.SelectObject(wxNullBitmap); + return square; +} + +//----------------------------------------------------------------------------- +// mpInfoLayer +//----------------------------------------------------------------------------- +IMPLEMENT_DYNAMIC_CLASS(mpInfoLayer, mpLayer) + +mpInfoLayer::mpInfoLayer() +{ + m_dim = wxRect(0,0,1,1); + m_brush = *wxTRANSPARENT_BRUSH; + m_reference.x = 0; m_reference.y = 0; + m_winX = 1; //parent->GetScrX(); + m_winY = 1; //parent->GetScrY(); + m_type = mpLAYER_INFO; +} + +mpInfoLayer::mpInfoLayer(wxRect rect, const wxBrush* brush) : m_dim(rect) +{ + m_brush = *brush; + m_reference.x = rect.x; + m_reference.y = rect.y; + m_winX = 1; //parent->GetScrX(); + m_winY = 1; //parent->GetScrY(); + m_type = mpLAYER_INFO; +} + +mpInfoLayer::~mpInfoLayer() +{ + +} + +void mpInfoLayer::UpdateInfo(mpWindow& w, wxEvent& event) +{ + +} + +bool mpInfoLayer::Inside(wxPoint& point) +{ + return m_dim.Contains(point); +} + +void mpInfoLayer::Move(wxPoint delta) +{ + m_dim.SetX(m_reference.x + delta.x); + m_dim.SetY(m_reference.y + delta.y); +} + +void mpInfoLayer::UpdateReference() +{ + m_reference.x = m_dim.x; + m_reference.y = m_dim.y; +} + + +void mpInfoLayer::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + // Adjust relative position inside the window + int scrx = w.GetScrX(); + int scry = w.GetScrY(); + // Avoid dividing by 0 + if(scrx == 0) scrx=1; + if(scry == 0) scry=1; + + if ((m_winX != scrx) || (m_winY != scry)) { +#ifdef MATHPLOT_DO_LOGGING + // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to %d x %d"), m_winX, m_winY, scrx, scry); +#endif + if (m_winX != 1) m_dim.x = (int) floor((double)(m_dim.x*scrx/m_winX)); + if (m_winY != 1) { + m_dim.y = (int) floor((double)(m_dim.y*scry/m_winY)); + UpdateReference(); + } + // Finally update window size + m_winX = scrx; + m_winY = scry; + } + dc.SetPen(m_pen); +// wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG); +// wxBitmap image1(image0); +// wxBrush semiWhite(image1); + dc.SetBrush(m_brush); + dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height); + } +} + +wxPoint mpInfoLayer::GetPosition() +{ + return m_dim.GetPosition(); +} + +wxSize mpInfoLayer::GetSize() +{ + return m_dim.GetSize(); +} + +mpInfoCoords::mpInfoCoords() : mpInfoLayer() +{ + +} + +mpInfoCoords::mpInfoCoords(wxRect rect, const wxBrush* brush) : mpInfoLayer(rect, brush) +{ + +} + +mpInfoCoords::~mpInfoCoords() +{ + +} + +void mpInfoCoords::UpdateInfo(mpWindow& w, wxEvent& event) +{ + if (event.GetEventType() == wxEVT_MOTION) { + int mouseX = ((wxMouseEvent&)event).GetX(); + int mouseY = ((wxMouseEvent&)event).GetY(); +/* It seems that Windows port of wxWidgets don't support multi-line test to be drawn in a wxDC. + wxGTK instead works perfectly with it. + Info on wxForum: http://wxforum.shadonet.com/viewtopic.php?t=3451&highlight=drawtext+eol */ +#ifdef _WINDOWS + m_content.Printf(wxT("x = %f y = %f"), w.p2x(mouseX), w.p2y(mouseY)); +#else + m_content.Printf(wxT("x = %f\ny = %f"), w.p2x(mouseX), w.p2y(mouseY)); +#endif + } +} + +void mpInfoCoords::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + // Adjust relative position inside the window + int scrx = w.GetScrX(); + int scry = w.GetScrY(); + if ((m_winX != scrx) || (m_winY != scry)) { +#ifdef MATHPLOT_DO_LOGGING + // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to %d x %d"), m_winX, m_winY, scrx, scry); +#endif + if (m_winX != 1) m_dim.x = (int) floor((double)(m_dim.x*scrx/m_winX)); + if (m_winY != 1) { + m_dim.y = (int) floor((double)(m_dim.y*scry/m_winY)); + UpdateReference(); + } + // Finally update window size + m_winX = scrx; + m_winY = scry; + } + dc.SetPen(m_pen); +// wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG); +// wxBitmap image1(image0); +// wxBrush semiWhite(image1); + dc.SetBrush(m_brush); + dc.SetFont(m_font); + int textX, textY; + dc.GetMultiLineTextExtent(m_content, &textX, &textY); + if (m_dim.width < textX + 10) m_dim.width = textX + 10; + if (m_dim.height < textY + 10) m_dim.height = textY + 10; + dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height); + dc.DrawText(m_content, m_dim.x + 5, m_dim.y + 5); + } +} + +mpInfoLegend::mpInfoLegend() : mpInfoLayer() +{ + +} + +mpInfoLegend::mpInfoLegend(wxRect rect, const wxBrush* brush) : mpInfoLayer(rect, brush) +{ + +} + +mpInfoLegend::~mpInfoLegend() +{ + +} + +void mpInfoLegend::UpdateInfo(mpWindow& w, wxEvent& event) +{ + +} + +void mpInfoLegend::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + // Adjust relative position inside the window + int scrx = w.GetScrX(); + int scry = w.GetScrY(); + if ((m_winX != scrx) || (m_winY != scry)) { +#ifdef MATHPLOT_DO_LOGGING + // wxLogMessage(_("mpInfoLayer::Plot() screen size has changed from %d x %d to %d x %d"), m_winX, m_winY, scrx, scry); +#endif + if (m_winX != 1) m_dim.x = (int) floor((double)(m_dim.x*scrx/m_winX)); + if (m_winY != 1) { + m_dim.y = (int) floor((double)(m_dim.y*scry/m_winY)); + UpdateReference(); + } + // Finally update window size + m_winX = scrx; + m_winY = scry; + } +// wxImage image0(wxT("pixel.png"), wxBITMAP_TYPE_PNG); +// wxBitmap image1(image0); +// wxBrush semiWhite(image1); + dc.SetBrush(m_brush); + dc.SetFont(m_font); + const int baseWidth = (mpLEGEND_MARGIN*2 + mpLEGEND_LINEWIDTH); + int textX = baseWidth, textY = mpLEGEND_MARGIN; + int plotCount = 0; + int posY = 0; + int tmpX = 0, tmpY = 0; + mpLayer* ly = NULL; + wxPen lpen; + wxString label; + for (unsigned int p = 0; p < w.CountAllLayers(); p++) { + ly = w.GetLayer(p); + if ((ly->GetLayerType() == mpLAYER_PLOT) && (ly->IsVisible())) { + label = ly->GetName(); + dc.GetTextExtent(label, &tmpX, &tmpY); + textX = (textX > (tmpX + baseWidth)) ? textX : (tmpX + baseWidth + mpLEGEND_MARGIN); + textY += (tmpY); +#ifdef MATHPLOT_DO_LOGGING + // wxLogMessage(_("mpInfoLegend::Plot() Adding layer %d: %s"), p, label.c_str()); +#endif + } + } + dc.SetPen(m_pen); + dc.SetBrush(m_brush); + m_dim.width = textX; + if (textY != mpLEGEND_MARGIN) { // Don't draw any thing if there are no visible layers + textY += mpLEGEND_MARGIN; + m_dim.height = textY; + dc.DrawRectangle(m_dim.x, m_dim.y, m_dim.width, m_dim.height); + for (unsigned int p2 = 0; p2 < w.CountAllLayers(); p2++) { + ly = w.GetLayer(p2); + if ((ly->GetLayerType() == mpLAYER_PLOT) && (ly->IsVisible())) { + label = ly->GetName(); + lpen = ly->GetPen(); + dc.GetTextExtent(label, &tmpX, &tmpY); + dc.SetPen(lpen); + //textX = (textX > (tmpX + baseWidth)) ? textX : (tmpX + baseWidth); + //textY += (tmpY + mpLEGEND_MARGIN); + posY = m_dim.y + mpLEGEND_MARGIN + plotCount*tmpY + (tmpY>>1); + dc.DrawLine(m_dim.x + mpLEGEND_MARGIN, // X start coord + posY, // Y start coord + m_dim.x + mpLEGEND_LINEWIDTH + mpLEGEND_MARGIN, // X end coord + posY); + //dc.DrawRectangle(m_dim.x + 5, m_dim.y + 5 + plotCount*tmpY, 5, 5); + dc.DrawText(label, m_dim.x + baseWidth, m_dim.y + mpLEGEND_MARGIN + plotCount*tmpY); + plotCount++; + } + } + } + } +} + + + +//----------------------------------------------------------------------------- +// mpLayer implementations - functions +//----------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(mpFX, mpLayer) + +mpFX::mpFX(wxString name, int flags) +{ + SetName(name); + m_flags = flags; + m_type = mpLAYER_PLOT; +} + +void mpFX::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + + wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + wxCoord iy = 0; + if (m_pen.GetWidth() <= 1) + { + for (wxCoord i = startPx; i < endPx; ++i) + { + iy = w.y2p( GetY(w.p2x(i))); + // Draw the point only if you can draw outside margins or if the point is inside margins + if (m_drawOutsideMargins || ((iy >= minYpx) && (iy <= maxYpx))) + dc.DrawPoint(i, iy );// (wxCoord) ((w.GetPosY() - GetY( (double)i / w.GetScaleX() + w.GetPosX()) ) * w.GetScaleY())); + } + } + else + { + for (wxCoord i = startPx; i < endPx; ++i) + { + iy = w.y2p( GetY(w.p2x(i))); + // Draw the point only if you can draw outside margins or if the point is inside margins + if (m_drawOutsideMargins || ((iy >= minYpx) && (iy <= maxYpx))) + dc.DrawLine( i, iy, i, iy); + // wxCoord c = w.y2p( GetY(w.p2x(i)) ); //(wxCoord) ((w.GetPosY() - GetY( (double)i / w.GetScaleX() + w.GetPosX()) ) * w.GetScaleY()); + + } + } + + if (!m_name.IsEmpty() && m_showName) + { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + /*if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT) + tx = (w.GetScrX()>>1) - tx - 8; + else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER) + tx = -tx/2; + else + tx = -(w.GetScrX()>>1) + 8; + */ + if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT) + tx = (w.GetScrX() - tx) - w.GetMarginRight() - 8; + else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER) + tx = ((w.GetScrX() - w.GetMarginRight() - w.GetMarginLeft() - tx) / 2) + w.GetMarginLeft(); + else + tx = w.GetMarginLeft() + 8; + dc.DrawText( m_name, tx, w.y2p(GetY(w.p2x(tx))) ); // (wxCoord) ((w.GetPosY() - GetY( (double)tx / w.GetScaleX() + w.GetPosX())) * w.GetScaleY()) ); + } + } +} + +IMPLEMENT_ABSTRACT_CLASS(mpFY, mpLayer) + +mpFY::mpFY(wxString name, int flags) +{ + SetName(name); + m_flags = flags; + m_type = mpLAYER_PLOT; +} + +void mpFY::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + + wxCoord i, ix; + + wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + if (m_pen.GetWidth() <= 1) + { + for (i = minYpx; i < maxYpx; ++i) + { + ix = w.x2p(GetX(w.p2y(i))); + if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx))) + dc.DrawPoint(ix, i); + } + } + else + { + for (i=0;i< w.GetScrY(); ++i) + { + ix = w.x2p(GetX(w.p2y(i))); + if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx))) + dc.DrawLine(ix, i, ix, i); + // wxCoord c = w.x2p(GetX(w.p2y(i))); //(wxCoord) ((GetX( (double)i / w.GetScaleY() + w.GetPosY()) - w.GetPosX()) * w.GetScaleX()); + // dc.DrawLine(c, i, c, i); + } + } + + if (!m_name.IsEmpty() && m_showName) + { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + if ((m_flags & mpALIGNMASK) == mpALIGN_TOP) + ty = w.GetMarginTop() + 8; + else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER) + ty = ((w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom() - ty) / 2) + w.GetMarginTop(); + else + ty = w.GetScrY() - 8 - ty - w.GetMarginBottom(); + + dc.DrawText( m_name, w.x2p(GetX(w.p2y(ty))), ty ); // (wxCoord) ((GetX( (double)i / w.GetScaleY() + w.GetPosY()) - w.GetPosX()) * w.GetScaleX()), -ty); + } + } +} + +IMPLEMENT_ABSTRACT_CLASS(mpFXY, mpLayer) + +mpFXY::mpFXY(wxString name, int flags) +{ + SetName(name); + m_flags = flags; + m_type = mpLAYER_PLOT; +} + +void mpFXY::UpdateViewBoundary(wxCoord xnew, wxCoord ynew) +{ + // Keep track of how many points have been drawn and the bouding box + maxDrawX = (xnew > maxDrawX) ? xnew : maxDrawX; + minDrawX = (xnew < minDrawX) ? xnew : minDrawX; + maxDrawY = (maxDrawY > ynew) ? maxDrawY : ynew; + minDrawY = (minDrawY < ynew) ? minDrawY : ynew; + //drawnPoints++; +} + +void mpFXY::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + + double x, y; + // Do this to reset the counters to evaluate bounding box for label positioning + Rewind(); GetNextXY(x, y); + maxDrawX = x; minDrawX = x; maxDrawY = y; minDrawY = y; + //drawnPoints = 0; + Rewind(); + + wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + wxCoord ix = 0, iy = 0; + + if (!m_continuous) + { + // for some reason DrawPoint does not use the current pen, + // so we use DrawLine for fat pens + if (m_pen.GetWidth() <= 1) + { + while (GetNextXY(x, y)) + { + ix = w.x2p(x); + iy = w.y2p(y); + if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx) && (iy >= minYpx) && (iy <= maxYpx))) { + dc.DrawPoint(ix, iy); + UpdateViewBoundary(ix, iy); + }; + } + } + else + { + while (GetNextXY(x, y)) + { + ix = w.x2p(x); + iy = w.y2p(y); + if (m_drawOutsideMargins || ((ix >= startPx) && (ix <= endPx) && (iy >= minYpx) && (iy <= maxYpx))) { + dc.DrawLine(ix, iy, ix, iy); + UpdateViewBoundary(ix, iy); + } + // dc.DrawLine(cx, cy, cx, cy); + } + } + } + else + { + // Old code + wxCoord x0=0,c0=0; + bool first = TRUE; + std::vector<wxPoint> ptVector; + bool outUp, outDown; + outUp = outDown = true; + while (GetNextXY(x, y)) + { + wxCoord x1 = w.x2p(x); // (wxCoord) ((x - w.GetPosX()) * w.GetScaleX()); + wxCoord c1 = w.y2p(y); // (wxCoord) ((w.GetPosY() - y) * w.GetScaleY()); + if (first) + { + first=FALSE; + x0=x1;c0=c1; + } + if((x1 >= startPx)&&(x0 <= endPx)) { + outDown = (c0 > maxYpx) && (c1 > maxYpx); + outUp = (c0 < minYpx) && (c1 < minYpx); + if (!outUp && !outDown) { + //Thales Lima Oliveira 22/06/2015 + //Comentado para desaparecer a reta limite da curva (porque usar aquele limite??) + /*if (c1 != c0) { + if (c0 < minYpx) { + x0 = (int)(((float)(minYpx - c0))/((float)(c1 - c0))*(x1-x0)) + x0; + c0 = minYpx; + } + if (c0 > maxYpx) { + x0 = (int)(((float)(maxYpx - c0))/((float)(c1 - c0))*(x1-x0)) + x0; + //wxLogDebug(wxT("old x0 = %d, new x0 = %d"), x0, newX0); + //x0 = newX0; + c0 = maxYpx; + } + if (c1 < minYpx) { + x1 = (int)(((float)(minYpx - c0))/((float)(c1 - c0))*(x1-x0)) + x0; + c1 = minYpx; + } + if (c1 > maxYpx) { + x1 = (int)(((float)(maxYpx - c0))/((float)(c1 - c0))*(x1-x0)) + x0; + //wxLogDebug(wxT("old x0 = %d, old x1 = %d, new x1 = %d, c0 = %d, c1 = %d, maxYpx = %d"), x0, x1, newX1, c0, c1, maxYpx); + //x1 = newX1; + c1 = maxYpx; + } + } + if (x1 != x0) { + if (x0 < startPx) { + c0 = (int)(((float)(startPx - x0))/((float)(x1 -x0))*(c1 -c0)) + c0; + x0 = startPx; + } + if (x1 > endPx) { + c1 = (int)(((float)(endPx - x0))/((float)(x1 -x0))*(c1 -c0)) + c0; + x1 = endPx; + } + }*/ + + //dc.DrawLine(x0, c0, x1, c1); + //ptVector.push_back(wxPoint(x1, c1)); + UpdateViewBoundary(x1, c1); + } + } + ptVector.push_back(wxPoint(x1, c1)); + x0=x1; c0=c1; + } + + //modificado a forma de desenhar (desenha agora de forma contínua, permitindo usar diferentes tipos de traços) + wxPoint drawPts[ptVector.size()]; + for(int i = 0; i < (int)ptVector.size(); i++) { + drawPts[i] = ptVector[i]; + } + dc.DrawLines(ptVector.size(), drawPts); + + } + + if (!m_name.IsEmpty() && m_showName) + { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + // xxx implement else ... if (!HasBBox()) + { + // const int sx = w.GetScrX(); + // const int sy = w.GetScrY(); + + if ((m_flags & mpALIGNMASK) == mpALIGN_NW) + { + tx = minDrawX + 8; + ty = maxDrawY + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_NE) + { + tx = maxDrawX - tx - 8; + ty = maxDrawY + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_SE) + { + tx = maxDrawX - tx - 8; + ty = minDrawY - ty - 8; + } + else + { // mpALIGN_SW + tx = minDrawX + 8; + ty = minDrawY - ty - 8; + } + } + + dc.DrawText( m_name, tx, ty); + } + } +} + +//----------------------------------------------------------------------------- +// mpProfile implementation +//----------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(mpProfile, mpLayer) + +mpProfile::mpProfile(wxString name, int flags) +{ + SetName(name); + m_flags = flags; + m_type = mpLAYER_PLOT; +} + +void mpProfile::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + + wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + // Plot profile linking subsequent point of the profile, instead of mpFY, which plots simple points. + for (wxCoord i = startPx; i < endPx; ++i) { + wxCoord c0 = w.y2p( GetY(w.p2x(i)) ); // (wxCoord) ((w.GetYpos() - GetY( (double)i / w.GetXscl() + w.GetXpos()) ) * w.GetYscl()); + wxCoord c1 = w.y2p( GetY(w.p2x(i+1)) );//(wxCoord) ((w.GetYpos() - GetY( (double)(i+1) / w.GetXscl() + (w.GetXpos() ) ) ) * w.GetYscl()); + // c0 = (c0 <= maxYpx) ? ((c0 >= minYpx) ? c0 : minYpx) : maxYpx; + // c1 = (c1 <= maxYpx) ? ((c1 >= minYpx) ? c1 : minYpx) : maxYpx; + if (!m_drawOutsideMargins) { + c0 = (c0 <= maxYpx) ? ((c0 >= minYpx) ? c0 : minYpx) : maxYpx; + c1 = (c1 <= maxYpx) ? ((c1 >= minYpx) ? c1 : minYpx) : maxYpx; + } + dc.DrawLine(i, c0, i+1, c1); + }; + if (!m_name.IsEmpty()) { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + if ((m_flags & mpALIGNMASK) == mpALIGN_RIGHT) + tx = (w.GetScrX() - tx) - w.GetMarginRight() - 8; + else if ((m_flags & mpALIGNMASK) == mpALIGN_CENTER) + tx = ((w.GetScrX() - w.GetMarginRight() - w.GetMarginLeft() - tx) / 2) + w.GetMarginLeft(); + else + tx = w.GetMarginLeft() + 8; + + dc.DrawText( m_name, tx, w.y2p( GetY( w.p2x(tx) ) ) );//(wxCoord) ((w.GetPosY() - GetY( (double)tx / w.GetScaleX() + w.GetPosX())) * w.GetScaleY()) ); + } + } +} + +//----------------------------------------------------------------------------- +// mpLayer implementations - furniture (scales, ...) +//----------------------------------------------------------------------------- + +#define mpLN10 2.3025850929940456840179914546844 + +IMPLEMENT_DYNAMIC_CLASS(mpScaleX, mpLayer) + +mpScaleX::mpScaleX(wxString name, int flags, bool ticks, unsigned int type) +{ + SetName(name); + SetFont( (wxFont&) *wxSMALL_FONT); + SetPen( (wxPen&) *wxGREY_PEN); + m_flags = flags; + m_ticks = ticks; + m_labelType = type; + m_type = mpLAYER_AXIS; + m_labelFormat = wxT(""); +} + +void mpScaleX::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + dc.SetFont( m_font); + int orgy=0; + + const int extend = w.GetScrX(); // /2; + if (m_flags == mpALIGN_CENTER) + orgy = w.y2p(0); //(int)(w.GetPosY() * w.GetScaleY()); + if (m_flags == mpALIGN_TOP) { + if (m_drawOutsideMargins) + orgy = X_BORDER_SEPARATION; + else + orgy = w.GetMarginTop(); + } + if (m_flags == mpALIGN_BOTTOM) { + if (m_drawOutsideMargins) + orgy = X_BORDER_SEPARATION; + else + orgy = w.GetScrY() - w.GetMarginBottom(); + } + if (m_flags == mpALIGN_BORDER_BOTTOM ) + orgy = w.GetScrY() - 1;//dc.LogicalToDeviceY(0) - 1; + if (m_flags == mpALIGN_BORDER_TOP ) + orgy = 1;//-dc.LogicalToDeviceY(0); + + //Thales Lima Oliveira - 23/06/2015 + //Inserido retângulo para tapar a curva que fica fora do gráfico + dc.SetBrush( wxBrush(w.GetBackgroundColour()) ); + dc.SetPen( *wxTRANSPARENT_PEN ); + dc.DrawRectangle( 0, orgy, w.GetScrX(), orgy); + dc.DrawRectangle( 0, 0, w.GetScrX(), w.GetMarginTop()); + dc.SetPen( m_pen); + + dc.DrawLine( w.GetMarginLeft(), orgy, w.GetScrX() - w.GetMarginRight(), orgy); + + // To cut the axis line when draw outside margin is false, use this code + /*if (m_drawOutsideMargins == true) + dc.DrawLine( 0, orgy, w.GetScrX(), orgy); + else + dc.DrawLine( w.GetMarginLeft(), orgy, w.GetScrX() - w.GetMarginRight(), orgy); */ + + const double dig = floor( log( 128.0 / w.GetScaleX() ) / mpLN10 ); + const double step = exp( mpLN10 * dig); + const double end = w.GetPosX() + (double)extend / w.GetScaleX(); + + wxCoord tx, ty; + wxString s; + wxString fmt; + int tmp = (int)dig; + if (m_labelType == mpX_NORMAL) { + if (!m_labelFormat.IsEmpty()) { + fmt = m_labelFormat; + } else { + if (tmp>=1) { + fmt = wxT("%.f"); + } else { + tmp=8-tmp; + fmt.Printf(wxT("%%.%df"), tmp >= -1 ? 2 : -tmp); + } + } + } else { + // Date and/or time axis representation + if (m_labelType == mpX_DATETIME) { + fmt = (wxT("%04.0f-%02.0f-%02.0fT%02.0f:%02.0f:%02.0f")); + } else if (m_labelType == mpX_DATE) { + fmt = (wxT("%04.0f-%02.0f-%02.0f")); + } else if ((m_labelType == mpX_TIME) && (end/60 < 2)) { + fmt = (wxT("%02.0f:%02.3f")); + } else { + fmt = (wxT("%02.0f:%02.0f:%02.0f")); + } + } + + //double n = floor( (w.GetPosX() - (double)extend / w.GetScaleX()) / step ) * step ; + double n0 = floor( (w.GetPosX() /* - (double)(extend - w.GetMarginLeft() - w.GetMarginRight())/ w.GetScaleX() */) / step ) * step ; + double n = 0; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("mpScaleX::Plot: dig: %f , step: %f, end: %f, n: %f"), dig, step, end, n0); +#endif + wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + tmp=-65535; + int labelH = 0; // Control labels heigth to decide where to put axis name (below labels or on top of axis) + int maxExtent = 0; + for (n = n0; n < end; n += step) { + const int p = (int)((n - w.GetPosX()) * w.GetScaleX()); +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("mpScaleX::Plot: n: %f -> p = %d"), n, p); +#endif + if ((p >= startPx) && (p <= endPx)) { + if (m_ticks) { // draw axis ticks + if (m_flags == mpALIGN_BORDER_BOTTOM) + dc.DrawLine( p, orgy, p, orgy-4); + else + dc.DrawLine( p, orgy, p, orgy+4); + } else { // draw grid dotted lines + m_pen.SetStyle(wxDOT); + dc.SetPen(m_pen); + if ((m_flags == mpALIGN_BOTTOM) && !m_drawOutsideMargins) { + dc.DrawLine( p, orgy+4, p, minYpx ); + } else { + if ((m_flags == mpALIGN_TOP) && !m_drawOutsideMargins) { + dc.DrawLine( p, orgy-4, p, maxYpx ); + } else { + dc.DrawLine( p, 0/*-w.GetScrY()*/, p, w.GetScrY() ); + } + } + m_pen.SetStyle(wxSOLID); + dc.SetPen(m_pen); + } + // Write ticks labels in s string + if (m_labelType == mpX_NORMAL) + s.Printf(fmt, n); + else if (m_labelType == mpX_DATETIME) { + time_t when = (time_t)n; + struct tm tm = *localtime(&when); + s.Printf(fmt, (double)tm.tm_year+1900, (double)tm.tm_mon+1, (double)tm.tm_mday, (double)tm.tm_hour, (double)tm.tm_min, (double)tm.tm_sec); + } else if (m_labelType == mpX_DATE) { + time_t when = (time_t)n; + struct tm tm = *localtime(&when); + s.Printf(fmt, (double)tm.tm_year+1900, (double)tm.tm_mon+1, (double)tm.tm_mday); + } else if ((m_labelType == mpX_TIME) || (m_labelType == mpX_HOURS)) { + double modulus = fabs(n); + double sign = n/modulus; + double hh = floor(modulus/3600); + double mm = floor((modulus - hh*3600)/60); + double ss = modulus - hh*3600 - mm*60; + #ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("%02.0f Hours, %02.0f minutes, %02.0f seconds"), sign*hh, mm, ss); + #endif // MATHPLOT_DO_LOGGING + if (fmt.Len() == 20) // Format with hours has 11 chars + s.Printf(fmt, sign*hh, mm, floor(ss)); + else + s.Printf(fmt, sign*mm, ss); + } + dc.GetTextExtent(s, &tx, &ty); + labelH = (labelH <= ty) ? ty : labelH; +/* if ((p-tx/2-tmp) > 64) { // Problem about non-regular axis labels + if ((m_flags == mpALIGN_BORDER_BOTTOM) || (m_flags == mpALIGN_TOP)) { + dc.DrawText( s, p-tx/2, orgy-4-ty); + } else { + dc.DrawText( s, p-tx/2, orgy+4); + } + tmp=p+tx/2; + } + */ + maxExtent = (tx > maxExtent) ? tx : maxExtent; // Keep in mind max label width + } + } + // Actually draw labels, taking care of not overlapping them, and distributing them regularly + double labelStep = ceil((maxExtent + mpMIN_X_AXIS_LABEL_SEPARATION)/(w.GetScaleX()*step))*step; + for (n = n0; n < end; n += labelStep) { + const int p = (int)((n - w.GetPosX()) * w.GetScaleX()); +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("mpScaleX::Plot: n_label = %f -> p_label = %d"), n, p); +#endif + if ((p >= startPx) && (p <= endPx)) { + // Write ticks labels in s string + if (m_labelType == mpX_NORMAL) + s.Printf(fmt, n); + else if (m_labelType == mpX_DATETIME) { + time_t when = (time_t)n; + struct tm tm = *localtime(&when); + s.Printf(fmt, (double)tm.tm_year+1900, (double)tm.tm_mon+1, (double)tm.tm_mday, (double)tm.tm_hour, (double)tm.tm_min, (double)tm.tm_sec); + } else if (m_labelType == mpX_DATE) { + time_t when = (time_t)n; + struct tm tm = *localtime(&when); + s.Printf(fmt, (double)tm.tm_year+1900, (double)tm.tm_mon+1, (double)tm.tm_mday); + } else if ((m_labelType == mpX_TIME) || (m_labelType == mpX_HOURS)) { + double modulus = fabs(n); + double sign = n/modulus; + double hh = floor(modulus/3600); + double mm = floor((modulus - hh*3600)/60); + double ss = modulus - hh*3600 - mm*60; + #ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("%02.0f Hours, %02.0f minutes, %02.0f seconds"), sign*hh, mm, ss); + #endif // MATHPLOT_DO_LOGGING + if (fmt.Len() == 20) // Format with hours has 11 chars + s.Printf(fmt, sign*hh, mm, floor(ss)); + else + s.Printf(fmt, sign*mm, ss); + } + dc.GetTextExtent(s, &tx, &ty); + if ((m_flags == mpALIGN_BORDER_BOTTOM) || (m_flags == mpALIGN_TOP)) { + dc.DrawText( s, p-tx/2, orgy-4-ty); + } else { + dc.DrawText( s, p-tx/2, orgy+4); + } + } + } + + // Draw axis name + dc.GetTextExtent(m_name, &tx, &ty); + switch (m_flags) { + case mpALIGN_BORDER_BOTTOM: + dc.DrawText( m_name, extend - tx - 4, orgy - 8 - ty - labelH); + break; + case mpALIGN_BOTTOM: { + if ((!m_drawOutsideMargins) && (w.GetMarginBottom() > (ty + labelH + 8))) { + dc.DrawText( m_name, (endPx - startPx - tx)>>1, orgy + 6 + labelH); + } else { + dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty); + } + } break; + case mpALIGN_CENTER: + dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty); + break; + case mpALIGN_TOP: { + if ((!m_drawOutsideMargins) && (w.GetMarginTop() > (ty + labelH + 8))) { + dc.DrawText( m_name, (endPx - startPx - tx)>>1, orgy - 6 - ty - labelH); + } else { + dc.DrawText( m_name, extend - tx - 4, orgy + 4); + } + } break; + case mpALIGN_BORDER_TOP: + dc.DrawText( m_name, extend - tx - 4, orgy + 6 + labelH); + break; + default: + break; + } + } +/* if (m_flags != mpALIGN_TOP) { + + if ((m_flags == mpALIGN_BORDER_BOTTOM) || (m_flags == mpALIGN_TOP)) { + dc.DrawText( m_name, extend - tx - 4, orgy - 4 - (ty*2)); + } else { + dc.DrawText( m_name, extend - tx - 4, orgy - 4 - ty); //orgy + 4 + ty); + } + }; */ +} + +IMPLEMENT_DYNAMIC_CLASS(mpScaleY, mpLayer) + +mpScaleY::mpScaleY(wxString name, int flags, bool ticks) +{ + SetName(name); + SetFont( (wxFont&) *wxSMALL_FONT); + SetPen( (wxPen&) *wxGREY_PEN); + m_flags = flags; + m_ticks = ticks; + m_type = mpLAYER_AXIS; + m_labelFormat = wxT(""); +} + +void mpScaleY::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + dc.SetFont( m_font); + + int orgx=0; + const int extend = w.GetScrY(); // /2; + if (m_flags == mpALIGN_CENTER) + orgx = w.x2p(0); //(int)(w.GetPosX() * w.GetScaleX()); + if (m_flags == mpALIGN_LEFT) { + if (m_drawOutsideMargins) + orgx = Y_BORDER_SEPARATION; + else + orgx = w.GetMarginLeft(); + } + if (m_flags == mpALIGN_RIGHT) { + if (m_drawOutsideMargins) + orgx = w.GetScrX() - Y_BORDER_SEPARATION; + else + orgx = w.GetScrX() - w.GetMarginRight(); + } + if (m_flags == mpALIGN_BORDER_RIGHT ) + orgx = w.GetScrX() - 1; //dc.LogicalToDeviceX(0) - 1; + if (m_flags == mpALIGN_BORDER_LEFT ) + orgx = 1; //-dc.LogicalToDeviceX(0); + + dc.SetBrush( wxBrush(w.GetBackgroundColour()) ); + dc.SetPen( *wxTRANSPARENT_PEN ); + dc.DrawRectangle( 0, 0, orgx, extend); + dc.DrawRectangle( w.GetScrX() - w.GetMarginRight(), 0, w.GetScrX() - w.GetMarginRight(), extend); + dc.SetPen( m_pen); + + // Draw line + dc.DrawLine( orgx, w.GetMarginTop(), orgx, w.GetScrY() - w.GetMarginBottom()); + + // To cut the axis line when draw outside margin is false, use this code + /* if (m_drawOutsideMargins == true) + dc.DrawLine( orgx, 0, orgx, extend); + else + dc.DrawLine( orgx, w.GetMarginTop(), orgx, w.GetScrY() - w.GetMarginBottom()); */ + + const double dig = floor( log( 128.0 / w.GetScaleY() ) / mpLN10 ); + const double step = exp( mpLN10 * dig); + const double end = w.GetPosY() + (double)extend / w.GetScaleY(); + + wxCoord tx, ty; + wxString s; + wxString fmt; + int tmp = (int)dig; + double maxScaleAbs = fabs(w.GetDesiredYmax()); + double minScaleAbs = fabs(w.GetDesiredYmin()); + double endscale = (maxScaleAbs > minScaleAbs) ? maxScaleAbs : minScaleAbs; + if (m_labelFormat.IsEmpty()) { + if ((endscale < 1e4) && (endscale > 1e-3)) + fmt = wxT("%.2f"); + else + fmt = wxT("%.1e"); + } else { + fmt = m_labelFormat; + } + /* if (tmp>=1) + {*/ + // fmt = wxT("%7.5g"); + // } + // else + // { + // tmp=8-tmp; + // fmt.Printf(wxT("%%.%dg"), (tmp >= -1) ? 2 : -tmp); + // } + + double n = floor( (w.GetPosY() - (double)(extend - w.GetMarginTop() - w.GetMarginBottom())/ w.GetScaleY()) / step ) * step ; + + /* wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft(); */ + wxCoord endPx = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight(); + wxCoord minYpx = m_drawOutsideMargins ? 0 : w.GetMarginTop(); + wxCoord maxYpx = m_drawOutsideMargins ? w.GetScrY() : w.GetScrY() - w.GetMarginBottom(); + + tmp=65536; + int labelW = 0; + // Before staring cycle, calculate label height + int labelHeigth = 0; + s.Printf(fmt,n); + dc.GetTextExtent(s, &tx, &labelHeigth); + for (;n < end; n += step) { + const int p = (int)((w.GetPosY() - n) * w.GetScaleY()); + if ((p >= minYpx) && (p <= maxYpx)) { + if (m_ticks) { // Draw axis ticks + if (m_flags == mpALIGN_BORDER_LEFT) { + dc.DrawLine( orgx, p, orgx+4, p); + } else { + dc.DrawLine( orgx-4, p, orgx, p); //( orgx, p, orgx+4, p); + } + } else { + m_pen.SetStyle(wxDOT); + dc.SetPen( m_pen); + if ((m_flags == mpALIGN_LEFT) && !m_drawOutsideMargins) { + dc.DrawLine( orgx-4, p, endPx, p); + } else { + if ((m_flags == mpALIGN_RIGHT) && !m_drawOutsideMargins) { + dc.DrawLine( minYpx, p, orgx+4, p); + } else { + dc.DrawLine( 0/*-w.GetScrX()*/, p, w.GetScrX(), p); + } + } + m_pen.SetStyle(wxSOLID); + dc.SetPen( m_pen); + } + // Print ticks labels + s.Printf(fmt, n); + dc.GetTextExtent(s, &tx, &ty); +#ifdef MATHPLOT_DO_LOGGING + if (ty != labelHeigth) wxLogMessage(wxT("mpScaleY::Plot: ty(%f) and labelHeigth(%f) differ!"), ty, labelHeigth); +#endif + labelW = (labelW <= tx) ? tx : labelW; + if ((tmp-p+labelHeigth/2) > mpMIN_Y_AXIS_LABEL_SEPARATION) { + if ((m_flags == mpALIGN_BORDER_LEFT) || (m_flags == mpALIGN_RIGHT)) + dc.DrawText( s, orgx+4, p-ty/2); + else + dc.DrawText( s, orgx-4-tx, p-ty/2); //( s, orgx+4, p-ty/2); + tmp=p-labelHeigth/2; + } + } + } + // Draw axis name + + dc.GetTextExtent(m_name, &tx, &ty); + switch (m_flags) { + case mpALIGN_BORDER_LEFT: + dc.DrawText( m_name, labelW + 8, 4); + break; + case mpALIGN_LEFT: { + if ((!m_drawOutsideMargins) && (w.GetMarginLeft() > (ty + labelW + 8))) { + dc.DrawRotatedText( m_name, orgx - 6 - labelW - ty, (maxYpx - minYpx + tx)>>1, 90); + } else { + dc.DrawText( m_name, orgx + 4, 4); + } + } break; + case mpALIGN_CENTER: + dc.DrawText( m_name, orgx + 4, 4); + break; + case mpALIGN_RIGHT: { + if ((!m_drawOutsideMargins) && (w.GetMarginRight() > (ty + labelW + 8))) { + dc.DrawRotatedText( m_name, orgx + 6 + labelW, (maxYpx - minYpx + tx)>>1, 90); + } else { + dc.DrawText( m_name, orgx - tx - 4, 4); + } + } break; + case mpALIGN_BORDER_RIGHT: + dc.DrawText( m_name, orgx - 6 - tx -labelW, 4); + break; + default: + break; + } + } + +/* if (m_flags != mpALIGN_RIGHT) { + dc.GetTextExtent(m_name, &tx, &ty); + if (m_flags == mpALIGN_BORDER_LEFT) { + dc.DrawText( m_name, orgx-tx-4, -extend + ty + 4); + } else { + if (m_flags == mpALIGN_BORDER_RIGHT ) + dc.DrawText( m_name, orgx-(tx*2)-4, -extend + ty + 4); + else + dc.DrawText( m_name, orgx + 4, -extend + 4); + } + }; */ +} + +//----------------------------------------------------------------------------- +// mpWindow +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(mpWindow, wxWindow) + +BEGIN_EVENT_TABLE(mpWindow, wxWindow) + EVT_PAINT ( mpWindow::OnPaint) + EVT_SIZE ( mpWindow::OnSize) + EVT_SCROLLWIN_THUMBTRACK(mpWindow::OnScrollThumbTrack) + EVT_SCROLLWIN_PAGEUP(mpWindow::OnScrollPageUp) + EVT_SCROLLWIN_PAGEDOWN(mpWindow::OnScrollPageDown) + EVT_SCROLLWIN_LINEUP(mpWindow::OnScrollLineUp) + EVT_SCROLLWIN_LINEDOWN(mpWindow::OnScrollLineDown) + EVT_SCROLLWIN_TOP(mpWindow::OnScrollTop) + EVT_SCROLLWIN_BOTTOM(mpWindow::OnScrollBottom) + + EVT_MIDDLE_UP( mpWindow::OnShowPopupMenu) + EVT_RIGHT_DOWN( mpWindow::OnMouseRightDown) // JLB + EVT_RIGHT_UP ( mpWindow::OnShowPopupMenu) + EVT_MOUSEWHEEL( mpWindow::OnMouseWheel ) // JLB + EVT_MOTION( mpWindow::OnMouseMove ) // JLB + EVT_LEFT_DOWN( mpWindow::OnMouseLeftDown) + EVT_LEFT_UP( mpWindow::OnMouseLeftRelease) + + EVT_MENU( mpID_CENTER, mpWindow::OnCenter) + EVT_MENU( mpID_FIT, mpWindow::OnFit) + EVT_MENU( mpID_ZOOM_IN, mpWindow::OnZoomIn) + EVT_MENU( mpID_ZOOM_OUT, mpWindow::OnZoomOut) + EVT_MENU( mpID_LOCKASPECT,mpWindow::OnLockAspect) + EVT_MENU( mpID_HELP_MOUSE,mpWindow::OnMouseHelp) +END_EVENT_TABLE() + +mpWindow::mpWindow( wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long flag ) + : wxWindow( parent, id, pos, size, flag, wxT("mathplot") ) +{ + selectionRect = wxRect(0,0,0,0); + srColour = *wxBLACK; + + m_scaleX = m_scaleY = 1.0; + m_posX = m_posY = 0; + m_desiredXmin=m_desiredYmin=0; + m_desiredXmax=m_desiredYmax=1; + m_scrX = m_scrY = 64; // Fixed from m_scrX = m_scrX = 64; + m_minX = m_minY = 0; + m_maxX = m_maxY = 0; + m_last_lx= m_last_ly= 0; + m_buff_bmp = NULL; + m_enableDoubleBuffer = FALSE; + m_enableMouseNavigation = TRUE; + m_mouseMovedAfterRightClick = FALSE; + m_movingInfoLayer = NULL; + // Set margins to 0 + m_marginTop = 0; m_marginRight = 0; m_marginBottom = 0; m_marginLeft = 0; + + + m_lockaspect = FALSE; + + m_popmenu.Append( mpID_CENTER, _("Centralizar"), _("Centraliza a vizualização gráfico nesta posição")); + m_popmenu.Append( mpID_FIT, _("Encaixar"), _("Ajusta a vizualização para exibição de todos os itens")); + m_popmenu.Append( mpID_ZOOM_IN, _("Mais Zoom"), _("Mais Zoom na vizualização do gráfico.")); + m_popmenu.Append( mpID_ZOOM_OUT, _("Menos Zoom"), _("Menos Zoom na vizualização do gráfico.")); + m_popmenu.AppendCheckItem( mpID_LOCKASPECT, _("Bloquear proporções"), _("Bloquear as proporções do zoom horizontal e vertical.")); + m_popmenu.Append( mpID_HELP_MOUSE, _("Mostrar comandos do mouse..."), _("Exibe a ajuda sobre os comandos do mouse.")); + + m_layers.clear(); + SetBackgroundColour( *wxWHITE ); + m_bgColour = *wxWHITE; + m_fgColour = *wxBLACK; + + m_enableScrollBars = false; + SetSizeHints(128, 128); + + // J.L.Blanco: Eliminates the "flick" with the double buffer. + SetBackgroundStyle( wxBG_STYLE_CUSTOM ); + + UpdateAll(); +} + +mpWindow::~mpWindow() +{ + // Free all the layers: + DelAllLayers( true, false ); + + if (m_buff_bmp) + { + delete m_buff_bmp; + m_buff_bmp = NULL; + } +} + +// Mouse handler, for detecting when the user drag with the right button or just "clicks" for the menu +// JLB +void mpWindow::OnMouseRightDown(wxMouseEvent &event) +{ + m_mouseMovedAfterRightClick = FALSE; + m_mouseRClick_X = event.GetX(); + m_mouseRClick_Y = event.GetY(); + if (m_enableMouseNavigation) + { + SetCursor( *wxCROSS_CURSOR ); + } +} + +// Process mouse wheel events +// JLB +void mpWindow::OnMouseWheel( wxMouseEvent &event ) +{ + if (!m_enableMouseNavigation) + { + event.Skip(); + return; + } + +// GetClientSize( &m_scrX,&m_scrY); + + if (event.m_controlDown) + { + wxPoint clickPt( event.GetX(),event.GetY() ); + // CTRL key hold: Zoom in/out: + if (event.GetWheelRotation()>0) + ZoomIn( clickPt ); + else ZoomOut( clickPt ); + } + else + { + // Scroll vertically or horizontally (this is SHIFT is hold down). + int change = - event.GetWheelRotation(); // Opposite direction (More intuitive)! + double changeUnitsX = change / m_scaleX; + double changeUnitsY = change / m_scaleY; + + if (event.m_shiftDown) + { + m_posX += changeUnitsX; + m_desiredXmax += changeUnitsX; + m_desiredXmin += changeUnitsX; + } + else + { + m_posY -= changeUnitsY; + m_desiredYmax -= changeUnitsY; + m_desiredYmax -= changeUnitsY; + } + + UpdateAll(); + } +} + +// If the user "drags" with the right buttom pressed, do "pan" +// JLB +void mpWindow::OnMouseMove(wxMouseEvent &event) +{ + if (!m_enableMouseNavigation) + { + event.Skip(); + return; + } + + if (event.m_rightDown) + { + m_mouseMovedAfterRightClick = TRUE; // Hides the popup menu after releasing the button! + + // The change: + int Ax= m_mouseRClick_X - event.GetX(); + int Ay= m_mouseRClick_Y - event.GetY(); + + // For the next event, use relative to this coordinates. + m_mouseRClick_X = event.GetX(); + m_mouseRClick_Y = event.GetY(); + + double Ax_units = Ax / m_scaleX; + double Ay_units = -Ay / m_scaleY; + + m_posX += Ax_units; + m_posY += Ay_units; + m_desiredXmax += Ax_units; + m_desiredXmin += Ax_units; + m_desiredYmax += Ay_units; + m_desiredYmin += Ay_units; + + UpdateAll(); + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("[mpWindow::OnMouseMove] Ax:%i Ay:%i m_posX:%f m_posY:%f"),Ax,Ay,m_posX,m_posY); +#endif + } else { + if (event.m_leftDown) { + if (m_movingInfoLayer == NULL) { + /*wxClientDC dc(this); + wxPen pen(*wxBLACK, 1, wxDOT); + dc.SetPen(pen); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(m_mouseLClick_X, m_mouseLClick_Y, event.GetX() - m_mouseLClick_X, event.GetY() - m_mouseLClick_Y);*/ + selectionRect = wxRect(m_mouseLClick_X, m_mouseLClick_Y, event.GetX() - m_mouseLClick_X, event.GetY() - m_mouseLClick_Y); + } else { + wxPoint moveVector(event.GetX() - m_mouseLClick_X, event.GetY() - m_mouseLClick_Y); + m_movingInfoLayer->Move(moveVector); + } + UpdateAll(); + } else { + wxLayerList::iterator li; + for (li = m_layers.begin(); li != m_layers.end(); li++) { + if ((*li)->IsInfo() && (*li)->IsVisible()) { + mpInfoLayer* tmpLyr = (mpInfoLayer*) (*li); + tmpLyr->UpdateInfo(*this, event); + // UpdateAll(); + RefreshRect(tmpLyr->GetRectangle()); + } + } + /* if (m_coordTooltip) { + wxString toolTipContent; + toolTipContent.Printf(_("X = %f\nY = %f"), p2x(event.GetX()), p2y(event.GetY())); + wxTipWindow** ptr = NULL; + wxRect rectBounds(event.GetX(), event.GetY(), 5, 5); + wxTipWindow* tip = new wxTipWindow(this, toolTipContent, 100, ptr, &rectBounds); + + } */ + } + } + event.Skip(); +} + +void mpWindow::OnMouseLeftDown (wxMouseEvent &event) +{ + m_mouseLClick_X = event.GetX(); + m_mouseLClick_Y = event.GetY(); +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::OnMouseLeftDown() X = %d , Y = %d"), event.GetX(), event.GetY());/*m_mouseLClick_X, m_mouseLClick_Y);*/ +#endif + wxPoint pointClicked = event.GetPosition(); + m_movingInfoLayer = IsInsideInfoLayer(pointClicked); + if (m_movingInfoLayer != NULL) { +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::OnMouseLeftDown() started moving layer %lx"), (long int) m_movingInfoLayer);/*m_mouseLClick_X, m_mouseLClick_Y);*/ +#endif + } + /*/Modificação - Thales 22/06/2015 + if(event.Dragging() && m_movingInfoLayer == NULL) + { + wxClientDC dc(this); + wxPen pen(*wxBLACK, 1, wxDOT); + dc.SetPen(pen); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(m_mouseLClick_X, m_mouseLClick_Y, event.GetX() - m_mouseLClick_X, event.GetY() - m_mouseLClick_Y); + }*/ + event.Skip(); +} + +void mpWindow::OnMouseLeftRelease (wxMouseEvent &event) +{ + wxPoint release(event.GetX(), event.GetY()); + wxPoint press(m_mouseLClick_X, m_mouseLClick_Y); + if (m_movingInfoLayer != NULL) { + m_movingInfoLayer->UpdateReference(); + m_movingInfoLayer = NULL; + } else { + if (release != press) { + ZoomRect(press, release); + } /*else { + if (m_coordTooltip) { + wxString toolTipContent; + toolTipContent.Printf(_("X = %f\nY = %f"), p2x(event.GetX()), p2y(event.GetY())); + SetToolTip(toolTipContent); + } + } */ + } + selectionRect = wxRect(0,0,0,0); + event.Skip(); +} + +void mpWindow::Fit() +{ + if (UpdateBBox()) + Fit(m_minX ,m_maxX ,m_minY ,m_maxY ); +} + + +// JL +void mpWindow::Fit(double xMin, double xMax, double yMin, double yMax, wxCoord *printSizeX,wxCoord *printSizeY) +{ + // Save desired borders: + m_desiredXmin=xMin; m_desiredXmax=xMax; + m_desiredYmin=yMin; m_desiredYmax=yMax; + + if (printSizeX!=NULL && printSizeY!=NULL) + { + // Printer: + m_scrX = *printSizeX; + m_scrY = *printSizeY; + } + else + { + // Normal case (screen): + GetClientSize( &m_scrX,&m_scrY); + } + + double Ax,Ay; + + Ax = xMax - xMin; + Ay = yMax - yMin; + + m_scaleX = (Ax!=0) ? (m_scrX - m_marginLeft - m_marginRight)/Ax : 1; //m_scaleX = (Ax!=0) ? m_scrX/Ax : 1; + m_scaleY = (Ay!=0) ? (m_scrY - m_marginTop - m_marginBottom)/Ay : 1; //m_scaleY = (Ay!=0) ? m_scrY/Ay : 1; + + if (m_lockaspect) + { +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::Fit()(lock) m_scaleX=%f,m_scaleY=%f"), m_scaleX,m_scaleY); +#endif + // Keep the lowest "scale" to fit the whole range required by that axis (to actually "fit"!): + double s = m_scaleX < m_scaleY ? m_scaleX : m_scaleY; + m_scaleX = s; + m_scaleY = s; + } + + // Adjusts corner coordinates: This should be simply: + // m_posX = m_minX; + // m_posY = m_maxY; + // But account for centering if we have lock aspect: + m_posX = (xMin+xMax)/2 - ((m_scrX - m_marginLeft - m_marginRight)/2 + m_marginLeft)/m_scaleX ; // m_posX = (xMin+xMax)/2 - (m_scrX/2)/m_scaleX; +// m_posY = (yMin+yMax)/2 + ((m_scrY - m_marginTop - m_marginBottom)/2 - m_marginTop)/m_scaleY; // m_posY = (yMin+yMax)/2 + (m_scrY/2)/m_scaleY; + m_posY = (yMin+yMax)/2 + ((m_scrY - m_marginTop - m_marginBottom)/2 + m_marginTop)/m_scaleY; // m_posY = (yMin+yMax)/2 + (m_scrY/2)/m_scaleY; + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::Fit() m_desiredXmin=%f m_desiredXmax=%f m_desiredYmin=%f m_desiredYmax=%f"), xMin,xMax,yMin,yMax); + wxLogMessage(_("mpWindow::Fit() m_scaleX = %f , m_scrX = %d,m_scrY=%d, Ax=%f, Ay=%f, m_posX=%f, m_posY=%f"), m_scaleX, m_scrX,m_scrY, Ax,Ay,m_posX,m_posY); +#endif + + // It is VERY IMPORTANT to DO NOT call Refresh if we are drawing to the printer!! + // Otherwise, the DC dimensions will be those of the window instead of the printer device + if (printSizeX==NULL || printSizeY==NULL) + UpdateAll(); +} + +// Patch ngpaton +void mpWindow::DoZoomInXCalc (const int staticXpixel) +{ + // Preserve the position of the clicked point: + double staticX = p2x( staticXpixel ); + // Zoom in: + m_scaleX = m_scaleX * zoomIncrementalFactor; + // Adjust the new m_posx + m_posX = staticX - (staticXpixel / m_scaleX); + // Adjust desired + m_desiredXmin = m_posX; + m_desiredXmax = m_posX + (m_scrX - (m_marginLeft + m_marginRight)) / m_scaleX; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::DoZoomInXCalc() prior X coord: (%f), new X coord: (%f) SHOULD BE EQUAL!!"), staticX, p2x(staticXpixel)); +#endif +} + +void mpWindow::DoZoomInYCalc (const int staticYpixel) +{ + // Preserve the position of the clicked point: + double staticY = p2y( staticYpixel ); + // Zoom in: + m_scaleY = m_scaleY * zoomIncrementalFactor; + // Adjust the new m_posy: + m_posY = staticY + (staticYpixel / m_scaleY); + // Adjust desired + m_desiredYmax = m_posY; + m_desiredYmin = m_posY - (m_scrY - (m_marginTop + m_marginBottom)) / m_scaleY; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::DoZoomInYCalc() prior Y coord: (%f), new Y coord: (%f) SHOULD BE EQUAL!!"), staticY, p2y(staticYpixel)); +#endif +} + +void mpWindow::DoZoomOutXCalc (const int staticXpixel) +{ + // Preserve the position of the clicked point: + double staticX = p2x( staticXpixel ); + // Zoom out: + m_scaleX = m_scaleX / zoomIncrementalFactor; + // Adjust the new m_posx/y: + m_posX = staticX - (staticXpixel / m_scaleX); + // Adjust desired + m_desiredXmin = m_posX; + m_desiredXmax = m_posX + (m_scrX - (m_marginLeft + m_marginRight)) / m_scaleX; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::DoZoomOutXCalc() prior X coord: (%f), new X coord: (%f) SHOULD BE EQUAL!!"), staticX, p2x(staticXpixel)); +#endif +} + +void mpWindow::DoZoomOutYCalc (const int staticYpixel) +{ + // Preserve the position of the clicked point: + double staticY = p2y( staticYpixel ); + // Zoom out: + m_scaleY = m_scaleY / zoomIncrementalFactor; + // Adjust the new m_posx/y: + m_posY = staticY + (staticYpixel / m_scaleY); + // Adjust desired + m_desiredYmax = m_posY; + m_desiredYmin = m_posY - (m_scrY - (m_marginTop + m_marginBottom)) / m_scaleY; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::DoZoomOutYCalc() prior Y coord: (%f), new Y coord: (%f) SHOULD BE EQUAL!!"), staticY, p2y(staticYpixel)); +#endif +} + + +void mpWindow::ZoomIn(const wxPoint& centerPoint ) +{ + wxPoint c(centerPoint); + if (c == wxDefaultPosition) + { + GetClientSize(&m_scrX, &m_scrY); + c.x = (m_scrX - m_marginLeft - m_marginRight)/2 + m_marginLeft; // c.x = m_scrX/2; + c.y = (m_scrY - m_marginTop - m_marginBottom)/2 - m_marginTop; // c.y = m_scrY/2; +} + + // Preserve the position of the clicked point: + double prior_layer_x = p2x( c.x ); + double prior_layer_y = p2y( c.y ); + + // Zoom in: + m_scaleX = m_scaleX * zoomIncrementalFactor; + m_scaleY = m_scaleY * zoomIncrementalFactor; + + // Adjust the new m_posx/y: + m_posX = prior_layer_x - c.x / m_scaleX; + m_posY = prior_layer_y + c.y / m_scaleY; + + m_desiredXmin = m_posX; + m_desiredXmax = m_posX + (m_scrX - m_marginLeft - m_marginRight) / m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX; + m_desiredYmax = m_posY; + m_desiredYmin = m_posY - (m_scrY - m_marginTop - m_marginBottom) / m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY; + + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::ZoomIn() prior coords: (%f,%f), new coords: (%f,%f) SHOULD BE EQUAL!!"), prior_layer_x,prior_layer_y, p2x(c.x),p2y(c.y)); +#endif + + UpdateAll(); +} + +void mpWindow::ZoomOut(const wxPoint& centerPoint ) +{ + wxPoint c(centerPoint); + if (c == wxDefaultPosition) + { + GetClientSize(&m_scrX, &m_scrY); + c.x = (m_scrX - m_marginLeft - m_marginRight)/2 + m_marginLeft; // c.x = m_scrX/2; + c.y = (m_scrY - m_marginTop - m_marginBottom)/2 - m_marginTop; // c.y = m_scrY/2; + } + + // Preserve the position of the clicked point: + double prior_layer_x = p2x( c.x ); + double prior_layer_y = p2y( c.y ); + + // Zoom out: + m_scaleX = m_scaleX / zoomIncrementalFactor; + m_scaleY = m_scaleY / zoomIncrementalFactor; + + // Adjust the new m_posx/y: + m_posX = prior_layer_x - c.x / m_scaleX; + m_posY = prior_layer_y + c.y / m_scaleY; + + m_desiredXmin = m_posX; + m_desiredXmax = m_posX + (m_scrX - m_marginLeft - m_marginRight) / m_scaleX; // m_desiredXmax = m_posX + m_scrX / m_scaleX; + m_desiredYmax = m_posY; + m_desiredYmin = m_posY - (m_scrY - m_marginTop - m_marginBottom) / m_scaleY; // m_desiredYmin = m_posY - m_scrY / m_scaleY; + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::ZoomOut() prior coords: (%f,%f), new coords: (%f,%f) SHOULD BE EQUAL!!"), prior_layer_x,prior_layer_y, p2x(c.x),p2y(c.y)); +#endif + UpdateAll(); +} + +void mpWindow::ZoomInX() +{ + m_scaleX = m_scaleX * zoomIncrementalFactor; + UpdateAll(); +} + +void mpWindow::ZoomOutX() +{ + m_scaleX = m_scaleX / zoomIncrementalFactor; + UpdateAll(); +} + +void mpWindow::ZoomInY() +{ + m_scaleY = m_scaleY * zoomIncrementalFactor; + UpdateAll(); +} + +void mpWindow::ZoomOutY() +{ + m_scaleY = m_scaleY / zoomIncrementalFactor; + UpdateAll(); +} + +void mpWindow::ZoomRect(wxPoint p0, wxPoint p1) +{ + // Compute the 2 corners in graph coordinates: + double p0x = p2x(p0.x); + double p0y = p2y(p0.y); + double p1x = p2x(p1.x); + double p1y = p2y(p1.y); + + // Order them: + double zoom_x_min = p0x<p1x ? p0x:p1x; + double zoom_x_max = p0x>p1x ? p0x:p1x; + double zoom_y_min = p0y<p1y ? p0y:p1y; + double zoom_y_max = p0y>p1y ? p0y:p1y; + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("Zoom: (%f,%f)-(%f,%f)"),zoom_x_min,zoom_y_min,zoom_x_max,zoom_y_max); +#endif + + Fit(zoom_x_min,zoom_x_max,zoom_y_min,zoom_y_max); +} + +void mpWindow::LockAspect(bool enable) +{ + m_lockaspect = enable; + m_popmenu.Check(mpID_LOCKASPECT, enable); + + // Try to fit again with the new config: + Fit( m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax ); +} + +void mpWindow::OnShowPopupMenu(wxMouseEvent &event) +{ + // Only display menu if the user has not "dragged" the figure + if (m_enableMouseNavigation) + { + SetCursor( *wxSTANDARD_CURSOR ); + } + + if (!m_mouseMovedAfterRightClick) // JLB + { + m_clickedX = event.GetX(); + m_clickedY = event.GetY(); + PopupMenu( &m_popmenu, event.GetX(), event.GetY()); + } +} + +void mpWindow::OnLockAspect(wxCommandEvent& WXUNUSED(event)) +{ + LockAspect( !m_lockaspect ); +} + +void mpWindow::OnMouseHelp(wxCommandEvent& WXUNUSED(event)) +{ + wxMessageBox(_("Comandos suportados:\n \ + - Botão esquerdo e arrastar: Zoom no retângulo\n \ + - Botão direito e arrastar: Arrasta o gráfico\n \ + - Wheel: Deslocamento vertical\n \ + - Wheel + SHIFT: Deslocamento horizontal\n \ + - Wheel + CTRL: Mais Zoom/Menos Zoom"),_("Ajuda"),wxOK,this); +} + +void mpWindow::OnFit(wxCommandEvent& WXUNUSED(event)) +{ + Fit(); +} + +void mpWindow::OnCenter(wxCommandEvent& WXUNUSED(event)) +{ + GetClientSize(&m_scrX, &m_scrY); + int centerX = (m_scrX - m_marginLeft - m_marginRight)/2; // + m_marginLeft; // c.x = m_scrX/2; + int centerY = (m_scrY - m_marginTop - m_marginBottom)/2; // - m_marginTop; // c.y = m_scrY/2; + SetPos( p2x(m_clickedX - centerX), p2y(m_clickedY - centerY) ); + //SetPos( p2x(m_clickedX-m_scrX/2), p2y(m_clickedY-m_scrY/2) ); //SetPos( (double)(m_clickedX-m_scrX/2) / m_scaleX + m_posX, (double)(m_scrY/2-m_clickedY) / m_scaleY + m_posY); +} + +void mpWindow::OnZoomIn(wxCommandEvent& WXUNUSED(event)) +{ + ZoomIn( wxPoint(m_mouseRClick_X,m_mouseRClick_Y) ); +} + +void mpWindow::OnZoomOut(wxCommandEvent& WXUNUSED(event)) +{ + ZoomOut(); +} + +void mpWindow::OnSize( wxSizeEvent& WXUNUSED(event) ) +{ + // Try to fit again with the new window size: + Fit( m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax ); +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::OnSize() m_scrX = %d, m_scrY = %d"), m_scrX, m_scrY); +#endif // MATHPLOT_DO_LOGGING +} + +bool mpWindow::AddLayer( mpLayer* layer, bool refreshDisplay ) +{ + if (layer != NULL) { + m_layers.push_back( layer ); + if (refreshDisplay) UpdateAll(); + return true; + }; + return false; +} + +bool mpWindow::DelLayer( + mpLayer* layer, + bool alsoDeleteObject, + bool refreshDisplay ) +{ + wxLayerList::iterator layIt; + for (layIt = m_layers.begin(); layIt != m_layers.end(); layIt++) + { + if (*layIt == layer) + { + // Also delete the object? + if (alsoDeleteObject) + delete *layIt; + m_layers.erase(layIt); // this deleted the reference only + if (refreshDisplay) + UpdateAll(); + return true; + } + } + return false; +} + +void mpWindow::DelAllLayers( bool alsoDeleteObject, bool refreshDisplay) +{ + while ( m_layers.size()>0 ) + { + // Also delete the object? + if (alsoDeleteObject) delete m_layers[0]; + m_layers.erase( m_layers.begin() ); // this deleted the reference only + } + if (refreshDisplay) UpdateAll(); +} + +// void mpWindow::DoPrepareDC(wxDC& dc) +// { +// dc.SetDeviceOrigin(x2p(m_minX), y2p(m_maxY)); +// } + +void mpWindow::OnPaint( wxPaintEvent& WXUNUSED(event) ) +{ + wxPaintDC dc(this); + dc.GetSize(&m_scrX, &m_scrY); // This is the size of the visible area only! +// DoPrepareDC(dc); + +#ifdef MATHPLOT_DO_LOGGING + { + int px, py; + GetViewStart( &px, &py ); + wxLogMessage(_("[mpWindow::OnPaint] vis.area:%ix%i px=%i py=%i"),m_scrX,m_scrY,px,py); + } +#endif + + // Selects direct or buffered draw: + wxDC *trgDc; + + // J.L.Blanco @ Aug 2007: Added double buffer support + if (m_enableDoubleBuffer) + { + if (m_last_lx!=m_scrX || m_last_ly!=m_scrY) + { + if (m_buff_bmp) delete m_buff_bmp; + m_buff_bmp = new wxBitmap(m_scrX,m_scrY); + m_buff_dc.SelectObject(*m_buff_bmp); + m_last_lx=m_scrX; + m_last_ly=m_scrY; + } + trgDc = &m_buff_dc; + } + else + { + trgDc = &dc; + } + + // Draw background: + //trgDc->SetDeviceOrigin(0,0); + trgDc->SetPen( *wxTRANSPARENT_PEN ); + wxBrush brush( GetBackgroundColour() ); + trgDc->SetBrush( brush ); + trgDc->SetTextForeground(m_fgColour); + trgDc->DrawRectangle(0,0,m_scrX,m_scrY); + + // Draw all the layers: + //trgDc->SetDeviceOrigin( m_scrX>>1, m_scrY>>1); // Origin at the center + wxLayerList::iterator li; + for (li = m_layers.begin(); li != m_layers.end(); li++) + { + (*li)->Plot(*trgDc, *this); + }; + + // If doublebuffer, draw now to the window: + if (m_enableDoubleBuffer) + { + //trgDc->SetDeviceOrigin(0,0); + //dc.SetDeviceOrigin(0,0); // Origin at the center + dc.Blit(0,0,m_scrX,m_scrY,trgDc,0,0); + } + +/* if (m_coordTooltip) { + wxString toolTipContent; + wxPoint mousePoint = wxGetMousePosition(); + toolTipContent.Printf(_("X = %f\nY = %f"), p2x(mousePoint.x), p2y(mousePoint.y)); + SetToolTip(toolTipContent); + }*/ + // If scrollbars are enabled, refresh them + if (m_enableScrollBars) { +/* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX); + m_scrollY = (int) floor((m_maxY - m_posY )*m_scaleY); + Scroll(m_scrollX, m_scrollY);*/ + // Scroll(x2p(m_posX), y2p(m_posY)); +// SetVirtualSize((int) ((m_maxX - m_minX)*m_scaleX), (int) ((m_maxY - m_minY)*m_scaleY)); +// int centerX = (m_scrX - m_marginLeft - m_marginRight)/2; // + m_marginLeft; // c.x = m_scrX/2; +// int centerY = (m_scrY - m_marginTop - m_marginBottom)/2; // - m_marginTop; // c.y = m_scrY/2; + /*SetScrollbars(1, 1, (int) ((m_maxX - m_minX)*m_scaleX), (int) ((m_maxY - m_minY)*m_scaleY));*/ //, x2p(m_posX + centerX/m_scaleX), y2p(m_posY - centerY/m_scaleY), true); +} + wxPen pen(srColour, 1, wxDOT); + dc.SetPen(pen); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(selectionRect); +} + +// void mpWindow::OnScroll2(wxScrollWinEvent &event) +// { +// #ifdef MATHPLOT_DO_LOGGING +// wxLogMessage(_("[mpWindow::OnScroll2] Init: m_posX=%f m_posY=%f, sc_pos = %d"),m_posX,m_posY, event.GetPosition()); +// #endif +// // If scrollbars are not enabled, Skip operation +// if (!m_enableScrollBars) { +// event.Skip(); +// return; +// } +// // m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX); +// // m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY); +// // Scroll(m_scrollX, m_scrollY); +// +// // GetClientSize( &m_scrX, &m_scrY); +// //Scroll(x2p(m_desiredXmin), y2p(m_desiredYmin)); +// int pixelStep = 1; +// if (event.GetOrientation() == wxHORIZONTAL) { +// //m_desiredXmin -= (m_scrollX - event.GetPosition())/m_scaleX; +// //m_desiredXmax -= (m_scrollX - event.GetPosition())/m_scaleX; +// m_posX -= (m_scrollX - event.GetPosition())/m_scaleX; +// m_scrollX = event.GetPosition(); +// } +// Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax); +// // /* int pixelStep = 1; +// // if (event.GetOrientation() == wxHORIZONTAL) { +// // m_posX -= (px - event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX); +// // m_desiredXmax -= (px - event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX); +// // m_desiredXmin -= (px - event.GetPosition())/m_scaleX;//(pixelStep/m_scaleX); +// // //SetPosX( (double)px / GetScaleX() + m_minX + (double)(width>>1)/GetScaleX()); +// // // m_posX = p2x(px); //m_minX + (double)(px /*+ (m_scrX)*/)/GetScaleX(); +// // } else { +// // m_posY += (py - event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY); +// // m_desiredYmax += (py - event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY); +// // m_desiredYmax += (py - event.GetPosition())/m_scaleY;//(pixelStep/m_scaleY); +// // //SetPosY( m_maxY - (double)py / GetScaleY() - (double)(height>>1)/GetScaleY()); +// // //m_posY = m_maxY - (double)py / GetScaleY() - (double)(height>>1)/GetScaleY(); +// // // m_posY = p2y(py);//m_maxY - (double)(py /*+ (m_scrY)*/)/GetScaleY(); +// // }*/ +// #ifdef MATHPLOT_DO_LOGGING +// int px, py; +// GetViewStart( &px, &py); +// wxLogMessage(_("[mpWindow::OnScroll2] End: m_posX = %f, m_posY = %f, px = %f, py = %f"),m_posX, m_posY, px, py); +// #endif +// +// UpdateAll(); +// // event.Skip(); +// } + +void mpWindow::SetMPScrollbars(bool status) +{ + // Temporary behaviour: always disable scrollbars + m_enableScrollBars = status; //false; + if (status == false) + { + SetScrollbar(wxHORIZONTAL, 0, 0, 0); + SetScrollbar(wxVERTICAL, 0, 0, 0); + } + // else the scroll bars will be updated in UpdateAll(); + UpdateAll(); + +// EnableScrolling(false, false); +// m_enableScrollBars = status; +// EnableScrolling(status, status); +/* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX); + m_scrollY = (int) floor((m_posY - m_minY)*m_scaleY);*/ +// int scrollWidth = (int) floor((m_maxX - m_minX)*m_scaleX) - m_scrX; +// int scrollHeight = (int) floor((m_minY - m_maxY)*m_scaleY) - m_scrY; + +// /* m_scrollX = (int) floor((m_posX - m_minX)*m_scaleX); +// m_scrollY = (int) floor((m_maxY - m_posY /*- m_minY*/)*m_scaleY); +// int scrollWidth = (int) floor(((m_maxX - m_minX) - (m_desiredXmax - m_desiredXmin))*m_scaleX); +// int scrollHeight = (int) floor(((m_maxY - m_minY) - (m_desiredYmax - m_desiredYmin))*m_scaleY); +// #ifdef MATHPLOT_DO_LOGGING +// wxLogMessage(_("mpWindow::SetMPScrollbars() scrollWidth = %d, scrollHeight = %d"), scrollWidth, scrollHeight); +// #endif +// if(status) { +// SetScrollbars(1, +// 1, +// scrollWidth, +// scrollHeight, +// m_scrollX, +// m_scrollY); +// // SetVirtualSize((int) (m_maxX - m_minX), (int) (m_maxY - m_minY)); +// } +// Refresh(false);*/ +}; + +bool mpWindow::UpdateBBox() +{ + bool first = TRUE; + + for (wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); li++) + { + mpLayer* f = *li; + + if (f->HasBBox()) + { + if (first) + { + first = FALSE; + m_minX = f->GetMinX(); m_maxX=f->GetMaxX(); + m_minY = f->GetMinY(); m_maxY=f->GetMaxY(); + } + else + { + if (f->GetMinX()<m_minX) m_minX=f->GetMinX(); if (f->GetMaxX()>m_maxX) m_maxX=f->GetMaxX(); + if (f->GetMinY()<m_minY) m_minY=f->GetMinY(); if (f->GetMaxY()>m_maxY) m_maxY=f->GetMaxY(); + } + } + //node = node->GetNext(); + } +#ifdef MATHPLOT_DO_LOGGING + wxLogDebug(wxT("[mpWindow::UpdateBBox] Bounding box: Xmin = %f, Xmax = %f, Ymin = %f, YMax = %f"), m_minX, m_maxX, m_minY, m_maxY); +#endif // MATHPLOT_DO_LOGGING + return first == FALSE; +} + +// void mpWindow::UpdateAll() +// { + // GetClientSize( &m_scrX,&m_scrY); +/* if (m_enableScrollBars) { + // The "virtual size" of the scrolled window: + const int sx = (int)((m_maxX - m_minX) * GetScaleX()); + const int sy = (int)((m_maxY - m_minY) * GetScaleY()); + SetVirtualSize(sx, sy); + SetScrollRate(1, 1);*/ +// const int px = (int)((GetPosX() - m_minX) * GetScaleX());// - m_scrX); //(cx>>1)); + + // J.L.Blanco, Aug 2007: Formula fixed: +// const int py = (int)((m_maxY - GetPosY()) * GetScaleY());// - m_scrY); //(cy>>1)); +// int px, py; +// GetViewStart(&px0, &py0); +// px = (int)((m_posX - m_minX)*m_scaleX); +// py = (int)((m_maxY - m_posY)*m_scaleY); + +// SetScrollbars( 1, 1, sx - m_scrX, sy - m_scrY, px, py, TRUE); +// } + +// Working code +// UpdateBBox(); +// Refresh( FALSE ); +// end working code + +// Old version +/* bool box = UpdateBBox(); + if (box) +{ + int cx, cy; + GetClientSize( &cx, &cy); + + // The "virtual size" of the scrolled window: + const int sx = (int)((m_maxX - m_minX) * GetScaleX()); + const int sy = (int)((m_maxY - m_minY) * GetScaleY()); + + const int px = (int)((GetPosX() - m_minX) * GetScaleX() - (cx>>1)); + + // J.L.Blanco, Aug 2007: Formula fixed: + const int py = (int)((m_maxY - GetPosY()) * GetScaleY() - (cy>>1)); + + SetScrollbars( 1, 1, sx, sy, px, py, TRUE); + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("[mpWindow::UpdateAll] Size:%ix%i ScrollBars:%i,%i"),sx,sy,px,py); +#endif +} + + FitInside(); + Refresh( FALSE ); +*/ +// } + +void mpWindow::UpdateAll() +{ + if (UpdateBBox()) + { + if (m_enableScrollBars) + { + int cx, cy; + GetClientSize( &cx, &cy); + // Do x scroll bar + { + // Convert margin sizes from pixels to coordinates + double leftMargin = m_marginLeft / m_scaleX; + // Calculate the range in coords that we want to scroll over + double maxX = (m_desiredXmax > m_maxX) ? m_desiredXmax : m_maxX; + double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX; + if ((m_posX + leftMargin) < minX) + minX = m_posX + leftMargin; + // Calculate scroll bar size and thumb position + int sizeX = (int) ((maxX - minX) * m_scaleX); + int thumbX = (int)(((m_posX + leftMargin) - minX) * m_scaleX); + SetScrollbar(wxHORIZONTAL, thumbX, cx - (m_marginRight + m_marginLeft), sizeX); + } + // Do y scroll bar + { + // Convert margin sizes from pixels to coordinates + double topMargin = m_marginTop / m_scaleY; + // Calculate the range in coords that we want to scroll over + double maxY = (m_desiredYmax > m_maxY) ? m_desiredYmax : m_maxY; + if ((m_posY - topMargin) > maxY) + maxY = m_posY - topMargin; + double minY = (m_desiredYmin < m_minY) ? m_desiredYmin : m_minY; + // Calculate scroll bar size and thumb position + int sizeY = (int)((maxY - minY) * m_scaleY); + int thumbY = (int)((maxY - (m_posY - topMargin)) * m_scaleY); + SetScrollbar(wxVERTICAL, thumbY, cy - (m_marginTop + m_marginBottom), sizeY); + } + } + } + + Refresh( FALSE ); +} + +void mpWindow::DoScrollCalc (const int position, const int orientation) +{ + if (orientation == wxVERTICAL) + { + // Y axis + // Get top margin in coord units + double topMargin = m_marginTop / m_scaleY; + // Calculate maximum Y coord to be shown in the graph + double maxY = m_desiredYmax > m_maxY ? m_desiredYmax : m_maxY; + // Set new position + SetPosY((maxY - (position / m_scaleY)) + topMargin); + } + else + { + // X Axis + // Get left margin in coord units + double leftMargin = m_marginLeft / m_scaleX; + // Calculate minimum X coord to be shown in the graph + double minX = (m_desiredXmin < m_minX) ? m_desiredXmin : m_minX; + // Set new position + SetPosX((minX + (position / m_scaleX)) - leftMargin); + } +} + +void mpWindow::OnScrollThumbTrack (wxScrollWinEvent &event) +{ + DoScrollCalc(event.GetPosition(), event.GetOrientation()); +} + +void mpWindow::OnScrollPageUp (wxScrollWinEvent &event) +{ + int scrollOrientation = event.GetOrientation(); + // Get position before page up + int position = GetScrollPos(scrollOrientation); + // Get thumb size + int thumbSize = GetScrollThumb(scrollOrientation); + // Need to adjust position by a page + position -= thumbSize; + if (position < 0) + position = 0; + + DoScrollCalc(position, scrollOrientation); +} +void mpWindow::OnScrollPageDown (wxScrollWinEvent &event) +{ + int scrollOrientation = event.GetOrientation(); + // Get position before page up + int position = GetScrollPos(scrollOrientation); + // Get thumb size + int thumbSize = GetScrollThumb(scrollOrientation); + // Get scroll range + int scrollRange = GetScrollRange(scrollOrientation); + // Need to adjust position by a page + position += thumbSize; + if (position > (scrollRange - thumbSize)) + position = scrollRange - thumbSize; + + DoScrollCalc(position, scrollOrientation); +} + +void mpWindow::OnScrollLineUp (wxScrollWinEvent &event) +{ + int scrollOrientation = event.GetOrientation(); + // Get position before page up + int position = GetScrollPos(scrollOrientation); + // Need to adjust position by a line + position -= mpSCROLL_NUM_PIXELS_PER_LINE; + if (position < 0) + position = 0; + + DoScrollCalc(position, scrollOrientation); +} + +void mpWindow::OnScrollLineDown (wxScrollWinEvent &event) +{ + int scrollOrientation = event.GetOrientation(); + // Get position before page up + int position = GetScrollPos(scrollOrientation); + // Get thumb size + int thumbSize = GetScrollThumb(scrollOrientation); + // Get scroll range + int scrollRange = GetScrollRange(scrollOrientation); + // Need to adjust position by a page + position += mpSCROLL_NUM_PIXELS_PER_LINE; + if (position > (scrollRange - thumbSize)) + position = scrollRange - thumbSize; + + DoScrollCalc(position, scrollOrientation); +} + +void mpWindow::OnScrollTop(wxScrollWinEvent &event) +{ + DoScrollCalc(0, event.GetOrientation()); +} + +void mpWindow::OnScrollBottom(wxScrollWinEvent &event) +{ + int scrollOrientation = event.GetOrientation(); + // Get thumb size + int thumbSize = GetScrollThumb(scrollOrientation); + // Get scroll range + int scrollRange = GetScrollRange(scrollOrientation); + + DoScrollCalc(scrollRange - thumbSize, scrollOrientation); +} +// End patch ngpaton + +void mpWindow::SetScaleX(double scaleX) +{ + if (scaleX!=0) m_scaleX=scaleX; + UpdateAll(); +} + +// New methods implemented by Davide Rondini + +unsigned int mpWindow::CountLayers() +{ + //wxNode *node = m_layers.GetFirst(); + unsigned int layerNo = 0; + for(wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); li++)//while(node) + { + if ((*li)->HasBBox()) layerNo++; + // node = node->GetNext(); + }; + return layerNo; +} + +mpLayer* mpWindow::GetLayer(int position) +{ + if ((position >= (int) m_layers.size()) || position < 0) return NULL; + return m_layers[position]; +} + +mpLayer* mpWindow::GetLayerByName( const wxString &name) +{ + for (wxLayerList::iterator it=m_layers.begin();it!=m_layers.end();it++) + if (! (*it)->GetName().Cmp( name ) ) + return *it; + return NULL; // Not found +} + +void mpWindow::GetBoundingBox(double* bbox) +{ + bbox[0] = m_minX; + bbox[1] = m_maxX; + bbox[2] = m_minY; + bbox[3] = m_maxY; +} + +bool mpWindow::SaveScreenshot(const wxString& filename, wxBitmapType type, wxSize imageSize, bool fit) +{ + int sizeX, sizeY; + int bk_scrX, bk_scrY; + if (imageSize == wxDefaultSize) { + sizeX = m_scrX; + sizeY = m_scrY; + } else { + sizeX = imageSize.x; + sizeY = imageSize.y; + bk_scrX = m_scrX; + bk_scrY = m_scrY; + SetScr(sizeX, sizeY); + } + + wxBitmap screenBuffer(sizeX,sizeY); + wxMemoryDC screenDC; + screenDC.SelectObject(screenBuffer); + screenDC.SetPen( *wxTRANSPARENT_PEN ); + wxBrush brush( GetBackgroundColour() ); + screenDC.SetBrush( brush ); + screenDC.DrawRectangle(0,0,sizeX,sizeY); + + if (fit) { + Fit(m_minX, m_maxX, m_minY, m_maxY, &sizeX, &sizeY); + } else { + Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &sizeX, &sizeY); + } + // Draw all the layers: + wxLayerList::iterator li; + for (li = m_layers.begin(); li != m_layers.end(); li++) + (*li)->Plot(screenDC, *this); + + if (imageSize != wxDefaultSize) { + // Restore dimensions + SetScr(bk_scrX, bk_scrY); + Fit(m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax, &bk_scrX, &bk_scrY); + UpdateAll(); + } + // Once drawing is complete, actually save screen shot + wxImage screenImage = screenBuffer.ConvertToImage(); + return screenImage.SaveFile(filename, type); +} + +void mpWindow::SetMargins(int top, int right, int bottom, int left) +{ + m_marginTop = top; + m_marginRight = right; + m_marginBottom = bottom; + m_marginLeft = left; +} + +mpInfoLayer* mpWindow::IsInsideInfoLayer(wxPoint& point) +{ + wxLayerList::iterator li; + for (li = m_layers.begin(); li != m_layers.end(); li++) { +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::IsInsideInfoLayer() examinining layer = %p"), (*li)); +#endif // MATHPLOT_DO_LOGGING + if ((*li)->IsInfo()) { + mpInfoLayer* tmpLyr = (mpInfoLayer*) (*li); +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("mpWindow::IsInsideInfoLayer() layer = %p"), (*li)); +#endif // MATHPLOT_DO_LOGGING + if (tmpLyr->Inside(point)) { + return tmpLyr; + } + } + } + return NULL; +} + +void mpWindow::SetLayerVisible(const wxString &name, bool viewable) +{ + mpLayer* lx = GetLayerByName(name); + if ( lx ) { + lx->SetVisible(viewable); + UpdateAll(); + } +} + +bool mpWindow::IsLayerVisible(const wxString &name ) +{ + mpLayer* lx = GetLayerByName(name); + return (lx) ? lx->IsVisible() : false; +} + +void mpWindow::SetLayerVisible(const unsigned int position, bool viewable) +{ + mpLayer* lx = GetLayer(position); + if ( lx ) { + lx->SetVisible(viewable); + UpdateAll(); + } +} + +bool mpWindow::IsLayerVisible(const unsigned int position ) +{ + mpLayer* lx = GetLayer(position); + return (lx) ? lx->IsVisible() : false; +} + +void mpWindow::SetColourTheme(const wxColour& bgColour, const wxColour& drawColour, const wxColour& axesColour) +{ + SetBackgroundColour(bgColour); + SetForegroundColour(drawColour); + m_bgColour = bgColour; + m_fgColour = drawColour; + m_axColour = axesColour; + // cycle between layers to set colours and properties to them + wxLayerList::iterator li; + for (li = m_layers.begin(); li != m_layers.end(); li++) { + if ((*li)->GetLayerType() == mpLAYER_AXIS) { + wxPen axisPen = (*li)->GetPen(); // Get the old pen to modify only colour, not style or width + axisPen.SetColour(axesColour); + (*li)->SetPen(axisPen); + } + if ((*li)->GetLayerType() == mpLAYER_INFO) { + wxPen infoPen = (*li)->GetPen(); // Get the old pen to modify only colour, not style or width + infoPen.SetColour(drawColour); + (*li)->SetPen(infoPen); + } + } + + srColour = drawColour; +} + +// void mpWindow::EnableCoordTooltip(bool value) +// { +// m_coordTooltip = value; +// // if (value) GetToolTip()->SetDelay(100); +// } + +/* +double mpWindow::p2x(wxCoord pixelCoordX, bool drawOutside ) +{ + if (drawOutside) { + return m_posX + pixelCoordX/m_scaleX; + } + // Draw inside margins + double marginScaleX = ((double)(m_scrX - m_marginLeft - m_marginRight))/m_scrX; + return m_marginLeft + (m_posX + pixelCoordX/m_scaleX)/marginScaleX; +} + +double mpWindow::p2y(wxCoord pixelCoordY, bool drawOutside ) +{ + if (drawOutside) { + return m_posY - pixelCoordY/m_scaleY; + } + // Draw inside margins + double marginScaleY = ((double)(m_scrY - m_marginTop - m_marginBottom))/m_scrY; + return m_marginTop + (m_posY - pixelCoordY/m_scaleY)/marginScaleY; +} + +wxCoord mpWindow::x2p(double x, bool drawOutside) +{ + if (drawOutside) { + return (wxCoord) ((x-m_posX) * m_scaleX); + } + // Draw inside margins + double marginScaleX = ((double)(m_scrX - m_marginLeft - m_marginRight))/m_scrX; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("x2p ScrX = %d, marginRight = %d, marginLeft = %d, marginScaleX = %f"), m_scrX, m_marginRight, m_marginLeft, marginScaleX); +#endif // MATHPLOT_DO_LOGGING + return (wxCoord) (int)(((x-m_posX) * m_scaleX)*marginScaleX) - m_marginLeft; +} + +wxCoord mpWindow::y2p(double y, bool drawOutside) +{ + if (drawOutside) { + return (wxCoord) ( (m_posY-y) * m_scaleY); + } + // Draw inside margins + double marginScaleY = ((double)(m_scrY - m_marginTop - m_marginBottom))/m_scrY; +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("y2p ScrY = %d, marginTop = %d, marginBottom = %d, marginScaleY = %f"), m_scrY, m_marginTop, m_marginBottom, marginScaleY); +#endif // MATHPLOT_DO_LOGGING + return (wxCoord) ((int)((m_posY-y) * m_scaleY)*marginScaleY) - m_marginTop; +} +*/ + + +//----------------------------------------------------------------------------- +// mpFXYVector implementation - by Jose Luis Blanco (AGO-2007) +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(mpFXYVector, mpFXY) + +// Constructor +mpFXYVector::mpFXYVector(wxString name, int flags ) : mpFXY(name,flags) +{ + m_index = 0; + m_minX = -1; + m_maxX = 1; + m_minY = -1; + m_maxY = 1; + m_type = mpLAYER_PLOT; +} + +void mpFXYVector::Rewind() +{ + m_index = 0; +} + +bool mpFXYVector::GetNextXY(double & x, double & y) +{ + if (m_index>=m_xs.size()) + return FALSE; + else + { + x = m_xs[m_index]; + y = m_ys[m_index++]; + return m_index<=m_xs.size(); + } +} + +void mpFXYVector::Clear() +{ + m_xs.clear(); + m_ys.clear(); +} + +void mpFXYVector::SetData( const std::vector<double> &xs,const std::vector<double> &ys) +{ + // Check if the data vectora are of the same size + if (xs.size() != ys.size()) { + //wxLogError(_("wxMathPlot error: X and Y vector are not of the same length!")); + return; + } + // Copy the data: + m_xs = xs; + m_ys = ys; + + + // Update internal variables for the bounding box. + if (xs.size()>0) + { + m_minX = xs[0]; + m_maxX = xs[0]; + m_minY = ys[0]; + m_maxY = ys[0]; + + std::vector<double>::const_iterator it; + + for (it=xs.begin();it!=xs.end();it++) + { + if (*it<m_minX) m_minX=*it; + if (*it>m_maxX) m_maxX=*it; + } + for (it=ys.begin();it!=ys.end();it++) + { + if (*it<m_minY) m_minY=*it; + if (*it>m_maxY) m_maxY=*it; + } + //Thales Lima - 03/07/2015 -> diminuindo offset de y e retirando de x + /*m_minX-=0.5f; + m_minY-=0.5f; + m_maxX+=0.5f; + m_maxY+=0.5f;*/ + m_minY -= m_minY > 0.0 ? 0.005f*m_minY : -0.005f*m_minY;//0,5% de offset + m_maxY += m_maxY > 0.0 ? 0.005f*m_maxY : -0.005f*m_maxY; + } + else + { + m_minX = -1; + m_maxX = 1; + m_minY = -1; + m_maxY = 1; + } +} + +//----------------------------------------------------------------------------- +// mpText - provided by Val Greene +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(mpText, mpLayer) + + +/** @param name text to be displayed +@param offsetx x position in percentage (0-100) +@param offsetx y position in percentage (0-100) +*/ +mpText::mpText( wxString name, int offsetx, int offsety ) +{ + SetName(name); + + if (offsetx >= 0 && offsetx <= 100) + m_offsetx = offsetx; + else + m_offsetx = 5; + + if (offsety >= 0 && offsety <= 100) + m_offsety = offsety; + else + m_offsetx = 50; + m_type = mpLAYER_INFO; +} + +/** mpText Layer plot handler. +This implementation will plot the text adjusted to the visible area. +*/ + +void mpText::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen(m_pen); + dc.SetFont(m_font); + + wxCoord tw=0, th=0; + dc.GetTextExtent( GetName(), &tw, &th); + + // int left = -dc.LogicalToDeviceX(0); + // int width = dc.LogicalToDeviceX(0) - left; + // int bottom = dc.LogicalToDeviceY(0); + // int height = bottom - -dc.LogicalToDeviceY(0); + + /* dc.DrawText( GetName(), + (int)((((float)width/100.0) * m_offsety) + left - (tw/2)), + (int)((((float)height/100.0) * m_offsetx) - bottom) );*/ + int px = m_offsetx*(w.GetScrX() - w.GetMarginLeft() - w.GetMarginRight())/100; + int py = m_offsety*(w.GetScrY() - w.GetMarginTop() - w.GetMarginBottom())/100; + dc.DrawText( GetName(), px, py); + } +} + +//----------------------------------------------------------------------------- +// mpPrintout - provided by Davide Rondini +//----------------------------------------------------------------------------- + +mpPrintout::mpPrintout(mpWindow *drawWindow, const wxChar *title) : wxPrintout(title) +{ + drawn = false; + plotWindow = drawWindow; +} + +bool mpPrintout::OnPrintPage(int page) +{ + + wxDC *trgDc = GetDC(); + if ((trgDc) && (page == 1)) { + wxCoord m_prnX, m_prnY; + int marginX = 50; + int marginY = 50; + trgDc->GetSize(&m_prnX, &m_prnY); + + m_prnX -= (2*marginX); + m_prnY -= (2*marginY); + trgDc->SetDeviceOrigin(marginX, marginY); + +#ifdef MATHPLOT_DO_LOGGING + wxLogMessage(wxT("Print Size: %d x %d\n"), m_prnX, m_prnY); + wxLogMessage(wxT("Screen Size: %d x %d\n"), plotWindow->GetScrX(), plotWindow->GetScrY()); +#endif + + // Set the scale according to the page: + plotWindow->Fit( + plotWindow->GetDesiredXmin(), + plotWindow->GetDesiredXmax(), + plotWindow->GetDesiredYmin(), + plotWindow->GetDesiredYmax(), + &m_prnX, + &m_prnY ); + + // Get the colours of the plotWindow to restore them ath the end + wxColour oldBgColour = plotWindow->GetBackgroundColour(); + wxColour oldFgColour = plotWindow->GetForegroundColour(); + wxColour oldAxColour = plotWindow->GetAxesColour(); + + // Draw background, ensuring to use white background for printing. + trgDc->SetPen( *wxTRANSPARENT_PEN ); + // wxBrush brush( plotWindow->GetBackgroundColour() ); + wxBrush brush = *wxWHITE_BRUSH; + trgDc->SetBrush( brush ); + trgDc->DrawRectangle(0,0,m_prnX,m_prnY); + + // Draw all the layers: + //trgDc->SetDeviceOrigin( m_prnX>>1, m_prnY>>1); // Origin at the center + mpLayer *layer; + for (unsigned int li = 0; li < plotWindow->CountAllLayers(); li++) { + layer = plotWindow->GetLayer(li); + layer->Plot(*trgDc, *plotWindow); + }; + // Restore device origin + // trgDc->SetDeviceOrigin(0, 0); + // Restore colours + plotWindow->SetColourTheme(oldBgColour, oldFgColour, oldAxColour); + // Restore drawing + plotWindow->Fit(plotWindow->GetDesiredXmin(), plotWindow->GetDesiredXmax(), plotWindow->GetDesiredYmin(), plotWindow->GetDesiredYmax(), NULL, NULL); + plotWindow->UpdateAll(); + } + return true; +} + +bool mpPrintout::HasPage(int page) +{ + return (page == 1); +} + + +//----------------------------------------------------------------------------- +// mpMovableObject - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +void mpMovableObject::TranslatePoint( double x,double y, double &out_x, double &out_y ) +{ + double ccos = cos( m_reference_phi ); // Avoid computing cos/sin twice. + double csin = sin( m_reference_phi ); + + out_x = m_reference_x + ccos * x - csin * y; + out_y = m_reference_y + csin * x + ccos * y; +} + +// This method updates the buffers m_trans_shape_xs/ys, and the precomputed bounding box. +void mpMovableObject::ShapeUpdated() +{ + // Just in case... + if (m_shape_xs.size()!=m_shape_ys.size()) + { + //::wxLogError(wxT("[mpMovableObject::ShapeUpdated] Error, m_shape_xs and m_shape_ys have different lengths!")); + } + else + { + double ccos = cos( m_reference_phi ); // Avoid computing cos/sin twice. + double csin = sin( m_reference_phi ); + + m_trans_shape_xs.resize(m_shape_xs.size()); + m_trans_shape_ys.resize(m_shape_xs.size()); + + std::vector<double>::iterator itXi, itXo; + std::vector<double>::iterator itYi, itYo; + + m_bbox_min_x=1e300; + m_bbox_max_x=-1e300; + m_bbox_min_y=1e300; + m_bbox_max_y=-1e300; + + for (itXo=m_trans_shape_xs.begin(),itYo=m_trans_shape_ys.begin(),itXi=m_shape_xs.begin(),itYi=m_shape_ys.begin(); + itXo!=m_trans_shape_xs.end(); itXo++,itYo++,itXi++,itYi++) + { + *itXo = m_reference_x + ccos * (*itXi) - csin * (*itYi); + *itYo = m_reference_y + csin * (*itXi) + ccos * (*itYi); + + // Keep BBox: + if (*itXo < m_bbox_min_x) m_bbox_min_x = *itXo; + if (*itXo > m_bbox_max_x) m_bbox_max_x = *itXo; + if (*itYo < m_bbox_min_y) m_bbox_min_y = *itYo; + if (*itYo > m_bbox_max_y) m_bbox_max_y = *itYo; + } + } +} + +void mpMovableObject::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible) { + dc.SetPen( m_pen); + + + std::vector<double>::iterator itX=m_trans_shape_xs.begin(); + std::vector<double>::iterator itY=m_trans_shape_ys.begin(); + + if (!m_continuous) + { + // for some reason DrawPoint does not use the current pen, + // so we use DrawLine for fat pens + if (m_pen.GetWidth() <= 1) + { + while (itX!=m_trans_shape_xs.end()) + { + dc.DrawPoint( w.x2p(*(itX++)), w.y2p( *(itY++) ) ); + } + } + else + { + while (itX!=m_trans_shape_xs.end()) + { + wxCoord cx = w.x2p(*(itX++)); + wxCoord cy = w.y2p(*(itY++)); + dc.DrawLine(cx, cy, cx, cy); + } + } + } + else + { + wxCoord cx0=0,cy0=0; + bool first = TRUE; + while (itX!=m_trans_shape_xs.end()) + { + wxCoord cx = w.x2p(*(itX++)); + wxCoord cy = w.y2p(*(itY++)); + if (first) + { + first=FALSE; + cx0=cx;cy0=cy; + } + dc.DrawLine(cx0, cy0, cx, cy); + cx0=cx; cy0=cy; + } + } + + if (!m_name.IsEmpty() && m_showName) + { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + if (HasBBox()) + { + wxCoord sx = (wxCoord) (( m_bbox_max_x - w.GetPosX()) * w.GetScaleX()); + wxCoord sy = (wxCoord) ((w.GetPosY() - m_bbox_max_y ) * w.GetScaleY()); + + tx = sx - tx - 8; + ty = sy - 8 - ty; + } + else + { + const int sx = w.GetScrX()>>1; + const int sy = w.GetScrY()>>1; + + if ((m_flags & mpALIGNMASK) == mpALIGN_NE) + { + tx = sx - tx - 8; + ty = -sy + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_NW) + { + tx = -sx + 8; + ty = -sy + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_SW) + { + tx = -sx + 8; + ty = sy - 8 - ty; + } + else + { + tx = sx - tx - 8; + ty = sy - 8 - ty; + } + } + + dc.DrawText( m_name, tx, ty); + } + } +} + +//----------------------------------------------------------------------------- +// mpCovarianceEllipse - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- + +// Called to update the m_shape_xs, m_shape_ys vectors, whenever a parameter changes. +void mpCovarianceEllipse::RecalculateShape() +{ + m_shape_xs.clear(); + m_shape_ys.clear(); + + // Preliminar checks: + if (m_quantiles<0) { /*::wxLogError(wxT("[mpCovarianceEllipse] Error: quantiles must be non-negative"));*/ return; } + if (m_cov_00<0) { /*::wxLogError(wxT("[mpCovarianceEllipse] Error: cov(0,0) must be non-negative"));*/ return; } + if (m_cov_11<0) { /*::wxLogError(wxT("[mpCovarianceEllipse] Error: cov(1,1) must be non-negative"));*/ return; } + + m_shape_xs.resize( m_segments,0 ); + m_shape_ys.resize( m_segments,0 ); + + // Compute the two eigenvalues of the covariance: + // ------------------------------------------------- + double b = -m_cov_00 - m_cov_11; + double c = m_cov_00*m_cov_11 - m_cov_01*m_cov_01; + + double D = b*b - 4*c; + + if (D<0) { /*::wxLogError(wxT("[mpCovarianceEllipse] Error: cov is not positive definite"));*/ return; } + + double eigenVal0 =0.5*( -b + sqrt(D) ); + double eigenVal1 =0.5*( -b - sqrt(D) ); + + // Compute the two corresponding eigenvectors: + // ------------------------------------------------- + double eigenVec0_x,eigenVec0_y; + double eigenVec1_x,eigenVec1_y; + + if (fabs(eigenVal0 - m_cov_00)>1e-6) + { + double k1x = m_cov_01 / ( eigenVal0 - m_cov_00 ); + eigenVec0_y = 1; + eigenVec0_x = eigenVec0_y * k1x; + } + else + { + double k1y = m_cov_01 / ( eigenVal0 - m_cov_11 ); + eigenVec0_x = 1; + eigenVec0_y = eigenVec0_x * k1y; + } + + if (fabs(eigenVal1 - m_cov_00)>1e-6) + { + double k2x = m_cov_01 / ( eigenVal1 - m_cov_00 ); + eigenVec1_y = 1; + eigenVec1_x = eigenVec1_y * k2x; + } + else + { + double k2y = m_cov_01 / ( eigenVal1 - m_cov_11 ); + eigenVec1_x = 1; + eigenVec1_y = eigenVec1_x * k2y; + } + + // Normalize the eigenvectors: + double len = sqrt( eigenVec0_x*eigenVec0_x + eigenVec0_y*eigenVec0_y ); + eigenVec0_x /= len; // It *CANNOT* be zero + eigenVec0_y /= len; + + len = sqrt( eigenVec1_x*eigenVec1_x + eigenVec1_y*eigenVec1_y ); + eigenVec1_x /= len; // It *CANNOT* be zero + eigenVec1_y /= len; + + + // Take the sqrt of the eigenvalues (required for the ellipse scale): + eigenVal0 = sqrt(eigenVal0); + eigenVal1 = sqrt(eigenVal1); + + // Compute the 2x2 matrix M = diag(eigVal) * (~eigVec) (each eigen vector is a row): + double M_00 = eigenVec0_x * eigenVal0; + double M_01 = eigenVec0_y * eigenVal0; + + double M_10 = eigenVec1_x * eigenVal1; + double M_11 = eigenVec1_y * eigenVal1; + + // The points of the 2D ellipse: + double ang; + double Aang = 6.283185308/(m_segments-1); + int i; + for (i=0,ang=0;i<m_segments;i++,ang+= Aang ) + { + double ccos = cos(ang); + double csin = sin(ang); + + m_shape_xs[i] = m_quantiles * (ccos * M_00 + csin * M_10 ); + m_shape_ys[i] = m_quantiles * (ccos * M_01 + csin * M_11 ); + } // end for points on ellipse + + + ShapeUpdated(); +} + +//----------------------------------------------------------------------------- +// mpPolygon - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +void mpPolygon::setPoints( + const std::vector<double>& points_xs, + const std::vector<double>& points_ys, + bool closedShape ) +{ + if ( points_xs.size()!=points_ys.size() ) + { + //::wxLogError(wxT("[mpPolygon] Error: points_xs and points_ys must have the same number of elements")); + } + else + { + m_shape_xs = points_xs; + m_shape_ys = points_ys; + + if ( closedShape && points_xs.size()) + { + m_shape_xs.push_back( points_xs[0] ); + m_shape_ys.push_back( points_ys[0] ); + } + + ShapeUpdated(); + } +} + +//----------------------------------------------------------------------------- +// mpBitmapLayer - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +void mpBitmapLayer::GetBitmapCopy( wxImage &outBmp ) const +{ + if (m_validImg) + outBmp = m_bitmap; +} + +void mpBitmapLayer::SetBitmap( const wxImage &inBmp, double x, double y, double lx, double ly ) +{ + if (!inBmp.Ok()) + { + //::wxLogError(wxT("[mpBitmapLayer] Assigned bitmap is not Ok()!")); + } + else + { + m_bitmap = inBmp; //.GetSubBitmap( wxRect(0, 0, inBmp.GetWidth(), inBmp.GetHeight())); + m_min_x = x; + m_min_y = y; + m_max_x = x+lx; + m_max_y = y+ly; + m_validImg = true; + } +} + + +void mpBitmapLayer::Plot(wxDC & dc, mpWindow & w) +{ + if (m_visible && m_validImg) + { + /* 1st: We compute (x0,y0)-(x1,y1), the pixel coordinates of the real outer limits + of the image rectangle within the (screen) mpWindow. Note that these coordinates + might fall well far away from the real view limits when the user zoom in. + + 2nd: We compute (dx0,dy0)-(dx1,dy1), the pixel coordinates the rectangle that will + be actually drawn into the mpWindow, i.e. the clipped real rectangle that + avoids the non-visible parts. (offset_x,offset_y) are the pixel coordinates + that correspond to the window point (dx0,dy0) within the image "m_bitmap", and + (b_width,b_height) is the size of the bitmap patch that will be drawn. + + (x0,y0) ................. (x1,y0) + . . + . . + (x0,y1) ................ (x1,y1) + (In pixels!!) + */ + + // 1st step ------------------------------- + wxCoord x0 = w.x2p(m_min_x); + wxCoord y0 = w.y2p(m_max_y); + wxCoord x1 = w.x2p(m_max_x); + wxCoord y1 = w.y2p(m_min_y); + + // 2nd step ------------------------------- + // Precompute the size of the actual bitmap pixel on the screen (e.g. will be >1 if zoomed in) + double screenPixelX = ( x1-x0 ) / (double)m_bitmap.GetWidth(); + double screenPixelY = ( y1-y0 ) / (double)m_bitmap.GetHeight(); + + // The minimum number of pixels that the streched image will overpass the actual mpWindow borders: + wxCoord borderMarginX = (wxCoord)(screenPixelX+1); // ceil + wxCoord borderMarginY = (wxCoord)(screenPixelY+1); // ceil + + // The actual drawn rectangle (dx0,dy0)-(dx1,dy1) is (x0,y0)-(x1,y1) clipped: + wxCoord dx0=x0,dx1=x1,dy0=y0,dy1=y1; + if (dx0<0) dx0=-borderMarginX; + if (dy0<0) dy0=-borderMarginY; + if (dx1>w.GetScrX()) dx1=w.GetScrX()+borderMarginX; + if (dy1>w.GetScrY()) dy1=w.GetScrY()+borderMarginY; + + // For convenience, compute the width/height of the rectangle to be actually drawn: + wxCoord d_width = dx1-dx0+1; + wxCoord d_height = dy1-dy0+1; + + // Compute the pixel offsets in the internally stored bitmap: + wxCoord offset_x= (wxCoord) ( (dx0-x0)/screenPixelX ); + wxCoord offset_y= (wxCoord) ( (dy0-y0)/screenPixelY ); + + // and the size in pixel of the area to be actually drawn from the internally stored bitmap: + wxCoord b_width = (wxCoord) ( (dx1-dx0+1)/screenPixelX ); + wxCoord b_height = (wxCoord) ( (dy1-dy0+1)/screenPixelY ); + + #ifdef MATHPLOT_DO_LOGGING + wxLogMessage(_("[mpBitmapLayer::Plot] screenPixel: x=%f y=%f d_width=%ix%i"),screenPixelX,screenPixelY,d_width,d_height); + wxLogMessage(_("[mpBitmapLayer::Plot] offset: x=%i y=%i bmpWidth=%ix%i"),offset_x,offset_y,b_width,b_height); + #endif + + // Is there any visible region? + if (d_width>0 && d_height>0) + { + // Build the scaled bitmap from the image, only if it has changed: + if (m_scaledBitmap.GetWidth()!=d_width || + m_scaledBitmap.GetHeight()!=d_height || + m_scaledBitmap_offset_x != offset_x || + m_scaledBitmap_offset_y != offset_y ) + { + wxRect r(wxRect(offset_x,offset_y,b_width,b_height)); + // Just for the case.... + if (r.x<0) r.x=0; + if (r.y<0) r.y=0; + if (r.width>m_bitmap.GetWidth()) r.width=m_bitmap.GetWidth(); + if (r.height>m_bitmap.GetHeight()) r.height=m_bitmap.GetHeight(); + + m_scaledBitmap = wxBitmap( + wxBitmap(m_bitmap).GetSubBitmap( r ).ConvertToImage() + .Scale(d_width,d_height) ); + m_scaledBitmap_offset_x = offset_x; + m_scaledBitmap_offset_y = offset_y; + } + + // Draw it: + dc.DrawBitmap( m_scaledBitmap, dx0,dy0, true ); + } + } + + // Draw the name label + if (!m_name.IsEmpty() && m_showName) + { + dc.SetFont(m_font); + + wxCoord tx, ty; + dc.GetTextExtent(m_name, &tx, &ty); + + if (HasBBox()) + { + wxCoord sx = (wxCoord) (( m_max_x - w.GetPosX()) * w.GetScaleX()); + wxCoord sy = (wxCoord) ((w.GetPosY() - m_max_y ) * w.GetScaleY()); + + tx = sx - tx - 8; + ty = sy - 8 - ty; + } + else + { + const int sx = w.GetScrX()>>1; + const int sy = w.GetScrY()>>1; + + if ((m_flags & mpALIGNMASK) == mpALIGN_NE) + { + tx = sx - tx - 8; + ty = -sy + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_NW) + { + tx = -sx + 8; + ty = -sy + 8; + } + else if ((m_flags & mpALIGNMASK) == mpALIGN_SW) + { + tx = -sx + 8; + ty = sy - 8 - ty; + } + else + { + tx = sx - tx - 8; + ty = sy - 8 - ty; + } + } + + dc.DrawText( m_name, tx, ty); + } +} |