diff --git a/ctl/ctlSQLResult.cpp b/ctl/ctlSQLResult.cpp index d75b383..d37e729 100644 --- a/ctl/ctlSQLResult.cpp +++ b/ctl/ctlSQLResult.cpp @@ -20,6 +20,8 @@ #include "ctl/ctlSQLResult.h" #include "utils/sysSettings.h" #include "frm/frmExport.h" +#include "frm/mathplot.h" +#include "frm/frmPlot.h" @@ -412,6 +414,202 @@ wxString ctlSQLResult::CheckSelColumnDate() } return ss; } +wxDateTime parseDT(int &fmttype,const wxString dtStr) { + wxDateTime dt((time_t)-1); + if (fmttype == -1) { + if (dt.ParseISOCombined(dtStr, ' ')) { + fmttype = 0; + } + else if (dt.ParseDateTime(dtStr)) { + fmttype = 1; + } else fmttype = -1; + return dt; + } + else if (fmttype == 0) { + if (dt.ParseISOCombined(dtStr, ' ')) return dt; + else { + fmttype = -1; + return parseDT(fmttype,dtStr); + } + + } + else if (fmttype == 1) { + if (dt.ParseDateTime(dtStr)) return dt; + else { + fmttype = -1; + return parseDT(fmttype, dtStr); + } + } + return dt; +} +wxString ctlSQLResult::AutoColsPlot(int flags,frmQuery* parent) { + wxArrayInt cols; + wxArrayString leg; + wxArrayInt colsY; + if (IsSelection()) { + unsigned int i; + int row, col; + wxGridCellCoordsArray cells = GetSelectedCells(); + for (i = 0; i < cells.Count(); i++) { + //clearCellData(cells[i].GetRow(), cells[i].GetCol()); + //cols.Add(cells[i].GetCol()); + } + wxGridCellCoordsArray topLeft = GetSelectionBlockTopLeft(); + wxGridCellCoordsArray bottomRight = GetSelectionBlockBottomRight(); + for (i = 0; i < topLeft.Count(); i++) { + //for (row = topLeft[i].GetRow(); row <= bottomRight[i].GetRow(); row++) + { + for (col = topLeft[i].GetCol(); col <= bottomRight[i].GetCol(); col++) { + //clearCellData(row, col); + cols.Add(col); + } + } + } + wxArrayInt cls = GetSelectedCols(); + for (i = 0; i < cls.Count(); i++) { + if (cols.Index(cls[i])== wxNOT_FOUND) cols.Add(cls[i]); +// for (row = 1; row < GetNumberRows(); row++) { +// clearCellData(row, cols[i]); +// } + } + } + else { + return "not select cells or columns"; + } + + if (cols.Count()>0) { + //cols = GetSelectedCols(); + if (cols.Count() < 3) { + wxMessageBox("The number of selected columns must be more than 2\nExample: LXY or XYYYY...", "Plot"); + return ""; + } + } + int idx = 0; + int cl1 = cols[idx]; + int legC = -1; + int xC = -1; + int typeAxisX = mpX_DATETIME; + // Leg col + if (colTypClasses.Item(cl1) == PGTYPCLASS_STRING) { legC = cl1; idx++; } + cl1 = cols[idx]; + // X col + wxString xA, yA; + xA = ctlSQLGrid::GetColumnName(cl1); + if (colTypClasses.Item(cl1) == PGTYPCLASS_DATE) { xC = cl1; idx++; } + else if (colTypClasses.Item(cl1) == PGTYPCLASS_NUMERIC) { xC = cl1; typeAxisX = mpX_NORMAL; idx++; } + if (xC == -1) { wxMessageBox("The value type of column X must be a date or a number", "Plot"); return ""; } + // Y cols + for (size_t col = idx; col < cols.Count(); col++) + { + int cl = cols[col]; + if (colTypClasses.Item(cl) == PGTYPCLASS_NUMERIC) { + if (legC == -1) { + + } + colsY.Add(cl); + leg.Add(ctlSQLGrid::GetColumnName(cl)); + } + else { + wxMessageBox("The values of column Y must be numeric", "Plot"); + return ""; + } + } + if (legC != -1) yA = leg[0]; else yA = "Value"; + //frmQuery* q = &parent; + frmPlot* frame = new frmPlot(parent,"Plot"); + frame->ClearAndSetAxis(xA, typeAxisX, yA); + // Rows read + size_t numRows = GetNumberRows(); + wxString lg,currLg; + + if (legC !=-1) { + // 3 cols + std::vector x,y; + int fmttype = -1; + wxDateTime dt; + for (int i = 0; i < numRows; i++) + { + if (GetRowSize(i) == 0) continue; + currLg= GetCellValue(i, legC); + if (lg != currLg && !lg.IsEmpty()) { + frame->AddSeries(lg, x, y); + lg = wxEmptyString; + } + if (lg.IsEmpty()) { + x.clear(); + y.clear(); + lg = currLg; + } + //str.Append(GetExportLine(i, cols)); + wxString xVal = GetCellValue(i, xC); + wxString yVal = GetCellValue(i, colsY[0]); + double xv, yv; + //yv=wxAtof(yVal); + if (!yVal.ToCDouble(&yv)) { yVal.ToDouble(&yv); /* error! */ } + if (typeAxisX == mpX_DATETIME) { + dt = parseDT(fmttype, xVal); + if (fmttype == -1) { + wxString temp = wxString::Format("The values of column X must be timestamp [row: %d,col: %d]",i+1, colsY[0]); + wxMessageBox(temp, "Plot"); + return ""; + } + xv = (double)dt.GetTicks(); + } + else + { + if (!xVal.ToCDouble(&xv)) { xVal.ToDouble(&xv); /* error! */ } + } + x.push_back(xv); + y.push_back(yv); + } + frame->AddSeries(lg, x, y); + + } + else { + std::vector x, y; + int fmttype = -1; + wxDateTime dt; + bool first = true; + for (size_t col = 0; col < colsY.Count(); col++) + { + lg = leg[col]; + int ccol = colsY[col]; + for (int i = 0; i < numRows; i++) + { + if (GetRowSize(i) == 0) continue; + //str.Append(GetExportLine(i, cols)); + wxString yVal = GetCellValue(i, ccol); + double xv, yv; + if (!yVal.ToCDouble(&yv)) { yVal.ToDouble(&yv); /* error! */ } + if (first) { + wxString xVal = GetCellValue(i, xC); + if (typeAxisX == mpX_DATETIME) { + dt = parseDT(fmttype, xVal); + if (fmttype == -1) { + wxString temp = wxString::Format("The values of column X must be timestamp [row: %d,col: %d]", i, colsY[0]); + wxMessageBox(temp, "Plot"); + return ""; + } + xv = (double)dt.GetTicks(); + } + else + { + if (!xVal.ToCDouble(&xv)) { xVal.ToDouble(&xv); /* error! */ } + } + x.push_back(xv); + } + y.push_back(yv); + + } + frame->AddSeries(lg, x, y); + y.clear(); + first = false; + } + } + frame->Go(); + return "plot draw"; + +} wxString ctlSQLResult::SummaryColumn() { //ce=cells.Item(0); diff --git a/frm/frmPlot.cpp b/frm/frmPlot.cpp new file mode 100644 index 0000000..ab36636 --- /dev/null +++ b/frm/frmPlot.cpp @@ -0,0 +1,408 @@ + +#include "pgAdmin3.h" +#include "frm/frmPlot.h" + +enum { + ID_QUIT = 108, + ID_ABOUT, + ID_PRINT, + ID_PRINT_PREVIEW, + ID_ALIGN_X_AXIS, + ID_ALIGN_Y_AXIS, + ID_TOGGLE_GRID, + ID_TOGGLE_SCROLLBARS, + ID_TOGGLE_INFO, + ID_SAVE_SCREENSHOT, + ID_TOGGLE_LISSAJOUX, + ID_TOGGLE_SHOW_SIMBOL, + ID_TOGGLE_COSINE, + ID_BLACK_THEME +}; +//IMPLEMENT_DYNAMIC_CLASS(frmPlot, pgFrame) +static const struct wxColourDesc +{ + const wxChar* name; + // unsigned char r, g, b; +} +colors[] = +{ + wxT("#ED7D31") + ,wxT("#997300") + ,wxT("#A580D5") + ,wxT("#70AD47") + ,wxT("#FFC000") + ,wxT("#4472C4") + ,wxT("#70AD47") + ,wxT("#CA682A") + ,wxT("#FFE533") + ,wxT("#FF5933") + ,wxT("#FF59B9") + + ,wxT("#5A5FFF") + ,wxT("#5A9F8A") + ,wxT("#5A9F50") + ,wxT("#1CD749") + ,wxT("#6ABB49") + ,wxT("#BEBB49") + + + ,wxT("#4797D8") + ,wxT("#4713D8") + ,wxT("#7413D8") + ,wxT("#B389D8") + ,wxT("#9CDA89") + ,wxT("#E7D900") + + ,wxT("#FF9A00") + ,wxT("#00FF00") + ,wxT("#8A7DFF") + ,wxT("#250DFF") + ,wxT("#DA0D8F") + ,wxT("#17C3FF") + + +}; + +wxBEGIN_EVENT_TABLE(frmPlot, pgFrame) +// test +EVT_MENU(ID_ABOUT, frmPlot::OnAbout) +EVT_MENU(ID_QUIT, frmPlot::OnQuit) +EVT_MENU(ID_PRINT_PREVIEW, frmPlot::OnPrintPreview) +EVT_MENU(ID_PRINT, frmPlot::OnPrint) +EVT_MENU(mpID_FIT, frmPlot::OnFit) +EVT_MENU(ID_ALIGN_X_AXIS, frmPlot::OnAlignXAxis) +EVT_MENU(ID_ALIGN_Y_AXIS, frmPlot::OnAlignYAxis) +EVT_MENU(ID_TOGGLE_GRID, frmPlot::OnToggleGrid) +EVT_MENU(ID_TOGGLE_SCROLLBARS, frmPlot::OnCopy) +EVT_MENU(ID_TOGGLE_INFO, frmPlot::OnToggleInfoLayer) +EVT_MENU(ID_SAVE_SCREENSHOT, frmPlot::OnSaveScreenshot) +EVT_MENU(ID_BLACK_THEME, frmPlot::OnBlackTheme) +EVT_MENU(ID_TOGGLE_LISSAJOUX, frmPlot::OnToggleLissajoux) +EVT_MENU(ID_TOGGLE_SHOW_SIMBOL, frmPlot::OnToggleShowSimbol) +EVT_MENU(ID_TOGGLE_COSINE, frmPlot::OnToggleCosine) +EVT_CLOSE(frmPlot::OnClose) +wxEND_EVENT_TABLE() + + + +frmPlot::frmPlot(frmQuery* parent, const wxString& _title) : pgFrame(parent, _title) +{ + + dlgName = wxT("frmPlot"); + RestorePosition(-1, -1, 700, 500, 700, 500); + indexColor = 0; + //SetIcon(wxIcon(log_xpm)); + wxMenu* file_menu = new wxMenu(); + wxMenu* view_menu = new wxMenu(); + wxMenu* show_menu = new wxMenu(); + + file_menu->Append(ID_PRINT_PREVIEW, wxT("Print Pre&view...")); + file_menu->Append(ID_PRINT, wxT("&Print...")); + file_menu->Append(ID_SAVE_SCREENSHOT, wxT("Save screenshot")); + file_menu->Append(ID_TOGGLE_SCROLLBARS, wxT("Copy clipboard screenshot\tCtrl+C")); + + file_menu->AppendSeparator(); + file_menu->Append(ID_ABOUT, wxT("&About...")); + file_menu->Append(ID_QUIT, wxT("E&xit\tAlt-X")); + + view_menu->Append(mpID_FIT, wxT("&Fit bounding box"), wxT("Set plot view to show all items")); + view_menu->Append(mpID_ZOOM_IN, wxT("Zoom in"), wxT("Zoom in plot view.")); + view_menu->Append(mpID_ZOOM_OUT, wxT("Zoom out"), wxT("Zoom out plot view.")); + view_menu->AppendSeparator(); + view_menu->Append(ID_ALIGN_X_AXIS, wxT("Switch &X axis align")); + view_menu->Append(ID_ALIGN_Y_AXIS, wxT("Switch &Y axis align")); + view_menu->AppendCheckItem(ID_TOGGLE_GRID, wxT("Toggle grid/ticks")); + //view_menu->AppendCheckItem( ID_TOGGLE_SCROLLBARS, wxT("Show Scroll Bars")); + view_menu->AppendCheckItem(ID_TOGGLE_INFO, wxT("Show overlay info box")); + view_menu->Check(ID_TOGGLE_INFO, true); + view_menu->AppendCheckItem(ID_BLACK_THEME, wxT("Switch to black background theme")); + + //show_menu->AppendCheckItem(ID_TOGGLE_LISSAJOUX, wxT("Lissajoux")); + show_menu->AppendCheckItem(ID_TOGGLE_SHOW_SIMBOL, wxT("Show Simbol")); + //show_menu->AppendCheckItem(ID_TOGGLE_COSINE, wxT("Cosine")); + // Start with all plots visible + //show_menu->Check(ID_TOGGLE_LISSAJOUX, true); + show_menu->Check(ID_TOGGLE_SHOW_SIMBOL, true); + //show_menu->Check(ID_TOGGLE_COSINE, true); + + wxMenuBar* menu_bar = new wxMenuBar(); + menu_bar->Append(file_menu, wxT("&File")); + menu_bar->Append(view_menu, wxT("&View")); + menu_bar->Append(show_menu, wxT("&Show")); + + SetMenuBar(menu_bar); +// CreateStatusBar(1); + mpLayer* l; + + m_plot = new mpWindow(this, -1, wxPoint(0, 0), wxSize(100, 100), wxSUNKEN_BORDER); + + m_plot->SetMargins(30, 100, 50, 100); + wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL); + + topsizer->Add(m_plot, 1, wxEXPAND); +// topsizer->Add(m_log, 0, wxEXPAND); + + SetAutoLayout(TRUE); + SetSizer(topsizer); + axesPos[0] = 0; + axesPos[1] = 0; + ticks = true; + m_plot->Fit(); +} +frmPlot::~frmPlot() +{ + // If the status window wasn't launched in standalone mode... + //if (mainForm) mainForm->RemoveFrame(this); + + + // If connection is still available, delete it + SavePosition(); +} +void frmPlot::Go() +{ + // Show the window + m_plot->Fit(); + Show(true); +} + +void frmPlot::OnClose(wxCloseEvent& event) { + event.Skip(); + +} +void frmPlot::OnQuit(wxCommandEvent& WXUNUSED(event)) +{ + Close(TRUE); +} +void frmPlot::ClearAndSetAxis(wxString XtextAxis, unsigned int X_labelType, wxString YtextAxis) +{ + m_plot->DelAllLayers(true, false); + + mpScaleX* xaxis = new mpScaleX(XtextAxis, mpALIGN_BOTTOM, true, X_labelType); + mpScaleY* yaxis = new mpScaleY(YtextAxis, mpALIGN_LEFT, true); + wxFont graphFont(11, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL); + xaxis->SetFont(graphFont); + yaxis->SetFont(graphFont); + xaxis->SetDrawOutsideMargins(false); + yaxis->SetDrawOutsideMargins(false); + m_plot->AddLayer(xaxis); + m_plot->AddLayer(yaxis); + m_plot->AddLayer(nfo = new mpInfoCoords(wxRect(80, 20, 10, 10), wxTRANSPARENT_BRUSH)); //&hatch)); + nfo->SetVisible(true); + //wxBrush hatch2(wxColour(163,208,212), wxSOLID); + mpInfoLegend* leg; + m_plot->AddLayer(leg = new mpInfoLegend(wxRect(20, 20, 40, 40), wxTRANSPARENT_BRUSH)); //&hatch2)); + leg->SetVisible(true); + leg->SetDrawOutsideMargins(false); + indexColor = 0; + //m_plot->Fit(); +} + +void frmPlot::AddSeries(const wxString legend, const std::vector& x, const std::vector& y) +{ + mpFXYVector* vectorLayer = new mpFXYVector(legend); + vectorLayer->SetData(x, y); + vectorLayer->SetContinuity(true); + size_t n; + wxColourDesc cc; + for (n = indexColor; n < WXSIZEOF(colors); n++) + { + cc = colors[n]; + indexColor++; + break; + //= new wxColour(cc.name); + } + if (indexColor >= WXSIZEOF(colors)) cc.name = L"#000000"; + + wxPen vectorpen(wxColour(cc.name), 2, wxPENSTYLE_SOLID); + + vectorLayer->SetPen(vectorpen); + vectorLayer->SetDrawOutsideMargins(false); + vectorLayer->ShowName(false); + vectorLayer->ShowSimbol(true); + m_plot->AddLayer(vectorLayer); + //m_plot->Fit(); +} + +void frmPlot::OnFit(wxCommandEvent& WXUNUSED(event)) +{ + m_plot->Fit(); +} + +void frmPlot::OnAbout(wxCommandEvent& WXUNUSED(event)) +{ + wxMessageBox(wxT("wxWidgets mathplot sample\n(c) 2003 David Schalig\n(c) 2007-2009 Davide Rondini and wxMathPlot team")); +} + +void frmPlot::OnAlignXAxis(wxCommandEvent& WXUNUSED(event)) +{ + axesPos[0] = (int)(axesPos[0] + 1) % 5; + mpScaleX* xaxis = ((mpScaleX*)(m_plot->GetLayer(0))); + mpScaleY* yaxis = ((mpScaleY*)(m_plot->GetLayer(1))); + if (axesPos[0] == 0) { + xaxis->SetAlign(mpALIGN_BORDER_BOTTOM); + m_plot->SetMarginTop(0); + m_plot->SetMarginBottom(0); + } + if (axesPos[0] == 1) { + //((mpScaleX*)(m_plot->GetLayer(0)))->SetAlign(mpALIGN_BOTTOM); + xaxis->SetAlign(mpALIGN_BOTTOM); + m_plot->SetMarginTop(0); + m_plot->SetMarginBottom(50); + } + if (axesPos[0] == 2) { + //((mpScaleX*)(m_plot->GetLayer(0)))->SetAlign(mpALIGN_CENTER); + xaxis->SetAlign(mpALIGN_CENTER); + m_plot->SetMarginTop(0); + m_plot->SetMarginBottom(0); + } + if (axesPos[0] == 3) { + //((mpScaleX*)(m_plot->GetLayer(0)))->SetAlign(mpALIGN_TOP); + xaxis->SetAlign(mpALIGN_TOP); + m_plot->SetMarginTop(50); + m_plot->SetMarginBottom(0); + } + if (axesPos[0] == 4) { + ((mpScaleX*)(m_plot->GetLayer(0)))->SetAlign(mpALIGN_BORDER_TOP); + xaxis->SetAlign(mpALIGN_BORDER_TOP); + m_plot->SetMarginTop(0); + m_plot->SetMarginBottom(0); + } + m_plot->UpdateAll(); +} + +void frmPlot::OnAlignYAxis(wxCommandEvent& WXUNUSED(event)) +{ + axesPos[1] = (int)(axesPos[1] + 1) % 5; + mpScaleX* xaxis = ((mpScaleX*)(m_plot->GetLayer(0))); + mpScaleY* yaxis = ((mpScaleY*)(m_plot->GetLayer(1))); + if (axesPos[1] == 0) { + //((mpScaleY*)(m_plot->GetLayer(1)))->SetAlign(mpALIGN_BORDER_LEFT); + yaxis->SetAlign(mpALIGN_BORDER_LEFT); + m_plot->SetMarginLeft(0); + m_plot->SetMarginRight(0); + } + if (axesPos[1] == 1) { + //((mpScaleY*)(m_plot->GetLayer(1)))->SetAlign(mpALIGN_LEFT); + yaxis->SetAlign(mpALIGN_LEFT); + m_plot->SetMarginLeft(70); + m_plot->SetMarginRight(0); + } + if (axesPos[1] == 2) { + //((mpScaleY*)(m_plot->GetLayer(1)))->SetAlign(mpALIGN_CENTER); + yaxis->SetAlign(mpALIGN_CENTER); + m_plot->SetMarginLeft(0); + m_plot->SetMarginRight(0); + } + if (axesPos[1] == 3) { + //((mpScaleY*)(m_plot->GetLayer(1)))->SetAlign(mpALIGN_RIGHT); + yaxis->SetAlign(mpALIGN_RIGHT); + m_plot->SetMarginLeft(0); + m_plot->SetMarginRight(70); + } + if (axesPos[1] == 4) { + //((mpScaleY*)(m_plot->GetLayer(1)))->SetAlign(mpALIGN_BORDER_RIGHT); + yaxis->SetAlign(mpALIGN_BORDER_RIGHT); + m_plot->SetMarginLeft(0); + m_plot->SetMarginRight(0); + } + m_plot->UpdateAll(); +} + +void frmPlot::OnToggleGrid(wxCommandEvent& event) +{ + //ticks = !ticks; + ticks = !event.IsChecked(); + ((mpScaleX*)(m_plot->GetLayer(0)))->SetTicks(ticks); + ((mpScaleY*)(m_plot->GetLayer(1)))->SetTicks(ticks); + m_plot->UpdateAll(); +} + +void frmPlot::OnCopy(wxCommandEvent& event) +{ + // Write a bitmap to the clipboard wxImage image(wxT("splash.png"), wxBITMAP_TYPE_PNG); + wxBitmapType fileType = wxBITMAP_TYPE_PNG; +// wxSize imgSize(500, 500); + m_plot->SaveScreenshot("", fileType, wxDefaultSize, false,true); +} + +void frmPlot::OnToggleInfoLayer(wxCommandEvent& event) +{ + if (event.IsChecked()) + nfo->SetVisible(true); + else + nfo->SetVisible(false); + m_plot->UpdateAll(); + event.Skip(); +} + +void frmPlot::OnBlackTheme(wxCommandEvent& event) +{ + //wxColor black(0,0,0); + //wxColor white(255,255,255); + wxColour grey(96, 96, 96); + /*wxBrush* brush = new wxBrush(*wxTRANSPARENT_BRUSH)*/; + m_plot->SetColourTheme(*wxBLACK, *wxWHITE, grey); + m_plot->UpdateAll(); +} + +void frmPlot::OnPrintPreview(wxCommandEvent& WXUNUSED(event)) +{ + // Pass two printout objects: for preview, and possible printing. + mpPrintout* plotPrint = new mpPrintout(m_plot); + mpPrintout* plotPrintPreview = new mpPrintout(m_plot); + wxPrintPreview* preview = new wxPrintPreview(plotPrintPreview, plotPrint); + wxPreviewFrame* frame = new wxPreviewFrame(preview, this, wxT("Print Plot"), wxPoint(100, 100), wxSize(600, 650)); + frame->Centre(wxBOTH); + frame->Initialize(); + frame->Show(true); +} + +void frmPlot::OnPrint(wxCommandEvent& WXUNUSED(event)) +{ + wxPrinter printer; + mpPrintout printout(m_plot, wxT("Plot print")); + printer.Print(this, &printout, true); +} + +void frmPlot::OnSaveScreenshot(wxCommandEvent& event) +{ + wxFileDialog fileDialog(this, _("Save a screenshot"), wxT(""), wxT(""), wxT("BMP image (*.bmp) | *.bmp|JPEG image (*.jpg) | *.jpeg;*.jpg|PNG image (*.png) | *.png"), wxFD_SAVE); + if (fileDialog.ShowModal() == wxID_OK) { + wxFileName namePath(fileDialog.GetPath()); + wxBitmapType fileType = wxBITMAP_TYPE_BMP; + if (namePath.GetExt().CmpNoCase(wxT("jpeg")) == 0) fileType = wxBITMAP_TYPE_JPEG; + if (namePath.GetExt().CmpNoCase(wxT("jpg")) == 0) fileType = wxBITMAP_TYPE_JPEG; + if (namePath.GetExt().CmpNoCase(wxT("png")) == 0) fileType = wxBITMAP_TYPE_PNG; + wxSize imgSize(500, 500); + m_plot->SaveScreenshot(fileDialog.GetPath(), fileType, imgSize, false); + } + event.Skip(); +} + +void frmPlot::OnToggleLissajoux(wxCommandEvent& event) +{ + m_plot->SetLayerVisible(wxT("Lissajoux"), event.IsChecked()); +} + +void frmPlot::OnToggleShowSimbol(wxCommandEvent& event) +{ + //m_plot->SetLayerVisible(wxT("f(x) = SIN(x)"), event.IsChecked()); + mpLayer* ly = NULL; + wxPen lpen; + wxString label; + bool show = event.IsChecked(); + for (unsigned int p = 0; p < m_plot->CountAllLayers(); p++) { + ly = m_plot->GetLayer(p); + if ((ly->GetLayerType() == mpLAYER_PLOT)) { + mpFXYVector* ly = reinterpret_cast(m_plot->GetLayer(p)); + ly->ShowSimbol(show); + } + } + m_plot->UpdateAll(); +} + +void frmPlot::OnToggleCosine(wxCommandEvent& event) +{ + m_plot->SetLayerVisible(wxT("g(y) = COS(y)"), event.IsChecked()); +} + diff --git a/frm/frmQuery.cpp b/frm/frmQuery.cpp index 0198556..1e34b2e 100644 --- a/frm/frmQuery.cpp +++ b/frm/frmQuery.cpp @@ -124,6 +124,7 @@ BEGIN_EVENT_TABLE(frmQuery, pgFrame) EVT_MENU(MNU_COPY_WHERELIST, frmQuery::OnCopy_WhereList) EVT_MENU(MNU_CLEAR_FILTER, frmQuery::OnClear_Filter) EVT_MENU(MNU_CHECK_COLUMN_DATE, frmQuery::OnCheck_Column_Date) + EVT_MENU(MNU_AUTOCOLSPLOT, frmQuery::OnAutoColsPlot) EVT_MENU(MNU_FIND, frmQuery::OnSearchReplace) EVT_MENU(MNU_UNDO, frmQuery::OnUndo) EVT_MENU(MNU_REDO, frmQuery::OnRedo) @@ -2171,14 +2172,16 @@ void frmQuery::OnLabelRightClick(wxGridEvent &event) xmenu->Append(MNU_COPY, _("&Copy"), _("Copy selected cells to clipboard.")); xmenu->Append(MNU_PASTE, _("&Paste"), _("Paste data from the clipboard.")); xmenu->Append(MNU_DELETE, _("&Delete"), _("Delete selected rows.")); + xmenu->Append(MNU_AUTOCOLSPLOT, _("Draw plot LXY or XYYY..."), _("Draw plot LXY or XYYY...")); xmenu->Append(MNU_SUMMARY_COL, _("Summary"), _("Summary selected cells.")); xmenu->Append(MNU_CHECK_COLUMN_DATE, _("Check the sequence of dates"), _("Check the sequence of dates")); xmenu->Append(MNU_COPY_INSERT, _("Copy Insert format"), _("Copy Insert format.")); xmenu->Append(MNU_COPY_INLIST, _("IN LIST format copy"), _("Copy In list format.")); xmenu->Append(MNU_COPY_WHERELIST, _("WHERE LIST format copy"), _("Copy where list format.")); xmenu->Append(MNU_CLEAR_FILTER, _("Clear filter"), _("Clear filter")); - - xmenu->Enable(MNU_CHECK_COLUMN_DATE, sqlResult->GetSelectedCols().GetCount() > 0); + bool selcol = sqlResult->GetSelectedCols().GetCount() > 0; + xmenu->Enable(MNU_CHECK_COLUMN_DATE, selcol); + xmenu->Enable(MNU_AUTOCOLSPLOT, sqlResult->IsSelection()); xmenu->Enable(MNU_SUMMARY_COL, sqlResult->IsSelection()); xmenu->Enable(MNU_COPY_INLIST, sqlResult->IsSelection()); xmenu->Enable(MNU_COPY_WHERELIST, sqlResult->IsSelection()); @@ -2257,6 +2260,16 @@ void frmQuery::OnCheck_Column_Date(wxCommandEvent& ev) } } +void frmQuery::OnAutoColsPlot(wxCommandEvent& ev) +{ + // if (currentControl() == sqlResult) + { + frmQuery* q = this; + wxString s = sqlResult->AutoColsPlot(0, q); + SetStatusText(s, STATUSPOS_MSGS); + } +} + void frmQuery::OnSummary_Column(wxCommandEvent &ev) { // if (currentControl() == sqlResult) diff --git a/frm/mathplot.cpp b/frm/mathplot.cpp new file mode 100644 index 0000000..bf2b960 --- /dev/null +++ b/frm/mathplot.cpp @@ -0,0 +1,3183 @@ +///////////////////////////////////////////////////////////////////////////// +// 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 +///////////////////////////////////////////////////////////////////////////// + +// Comment out for release operation: +// (Added by J.L.Blanco, Aug 2007) +// #define MATHPLOT_DO_LOGGING +#include "pgAdmin3.h" +#include "frm/mathplot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include // used only for debug +#include // 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 10 + +// 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) { + m_pen = *wxBLACK_PEN; + m_font = *wxNORMAL_FONT; + m_continuous = FALSE; // Default + m_showName = TRUE; // Default + m_drawOutsideMargins = TRUE; + m_visible = true; + m_select = false; + m_brush = *wxTRANSPARENT_BRUSH; +} + +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_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(); + double xx=w.p2x(mouseX); + double yy=w.p2y(mouseY); + double tx,ty; + double dd; + + wxCoord sx,sy; + wxString namel; + for (unsigned int p = 0; p < w.CountAllLayers(); p++) { + mpFXYVector *ly = reinterpret_cast(w.GetLayer(p)); + if ((ly->GetLayerType() == mpLAYER_PLOT) && (ly->IsVisible())) { + tx=xx;ty=yy; + double d=ly->GetDistance(tx,ty); + sx= w.x2p(tx); + sy= w.y2p(ty); + dd=sqrt((double)(sx-mouseX)*(double)(sx-mouseX)+(double)(sy-mouseY)*(double)(sy-mouseY)); + if (dd<4) + { + namel=ly->GetName(); + mouseX=1; + break; + } + tx=0;ty=0; + } + } + if ((ty!=tx) && (tx!=0)) { + if (lastx != tx && lasty != ty) { + wxString toolTipContent; + + mpScaleX* xaxis = ((mpScaleX*)(w.GetLayer(0))); + mpScaleY* yaxis = ((mpScaleY*)(w.GetLayer(1))); + //double x=w.p2x(sx); + double x = tx; + wxString s = xaxis->GetLabelTextValue(x); + wxString xname = xaxis->GetName(); + wxString yname = yaxis->GetName(); + toolTipContent.Printf(_("%s\n%s = %s\n%s = %f"), namel,xname,s,yname, ty); + wxTipWindow** ptr = NULL; + wxRect rectBounds(sx, sy, 5, 5); + m_content = toolTipContent; + //wxTipWindow* tip = new wxTipWindow(w.GetParent(), toolTipContent, 100, ptr,&rectBounds); + lastx = tx; lasty = ty; + mouseX = sx; + mouseY = sy; + } + //SetVisible(true); + } else { + lastx = -1; + lasty = -1; + // SetVisible(false); + //m_dim.x=mouseX; + //m_dim.y=mouseY; + } + //m_content = wxEmptyString; +/* 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\nD=%f"), w.p2x(mouseX), w.p2y(mouseY),dd); +#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) { + //wxMouseEvent mv = &event; + wxPoint pointClicked = ((wxMouseEvent&)event).GetPosition(); + mpInfoLayer* m_movingInfoLayer = w.IsInsideInfoLayer(pointClicked); + if (m_movingInfoLayer != NULL) { + ///((mpInfoLegend&)m_movingInfoLayer).m_mouse_point = pointClicked; + m_mouse_point = pointClicked; + } +} + +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)); + // m_dim.x=scrx-w.GetMarginRight(); + // w.Refresh(); + 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); + m_dim.x = scrx - w.GetMarginRight(); + const int baseWidth = (mpLEGEND_MARGIN * 2 + mpLEGEND_LINEWIDTH); + int textX = baseWidth, textY = mpLEGEND_MARGIN; + 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; + w.SetMarginRight(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); + int plotCount = 0; + int posY = 0; + wxPoint pp(m_mouse_point.x - m_dim.x, m_mouse_point.y - m_dim.y); + 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(); + + //m_mouse_point + 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); + wxRect r; + bool ss = ly->IsSelect(); + r.x = 0; r.y = posY- m_dim.y; r.width = m_dim.width; r.height= tmpY; + if (r.Contains(pp)) { + wxPen pensel(lpen); + pensel.SetWidth(lpen.GetWidth() * 2); + dc.SetPen(pensel); + ly->SetSelect(true); + } else ly->SetSelect(false); + if (ss != ly->IsSelect()) { + w.Refresh(); + } + + 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) { + int wsimb = m_pen.GetWidth(); + if (IsSelect()) { + wxPen pensel(m_pen); + pensel.SetWidth(wsimb * 2); + dc.SetPen(pensel); + } + else { + + dc.SetPen(m_pen); + } + dc.SetBrush(m_brush); + 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; + 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)) { + bool outDown = (c0 > maxYpx) && (c1 > maxYpx); + bool outUp = (c0 < minYpx) && (c1 < minYpx); + if (!outUp && !outDown) { + 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); + if (m_simbol) dc.DrawRectangle(x1 - wsimb*2 / 2, c1 - wsimb*2 / 2, wsimb*2, wsimb*2); + UpdateViewBoundary(x1, c1); + } + } + x0 = x1; + c0 = c1; + } + } + + 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); + SetPen(DEFAULT_AXIS_TICK_COLOUR); + + m_flags = flags; + m_ticks = ticks; + m_labelType = type; + m_type = mpLAYER_AXIS; + m_labelFormat = wxT(""); +} +wxString mpScaleX::GetLabelTextValue(double &v) { + wxCoord tx, ty; + wxString s; + wxString fmt; + int tmp = 2; + 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.0f %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)) { + fmt = (wxT("%02.0f:%02.3f")); + } else { + fmt = (wxT("%02.0f:%02.0f:%02.0f")); + } + } + if (m_labelType == mpX_NORMAL) + s.Printf(fmt, v); + else if (m_labelType == mpX_DATETIME) { + time_t when = (time_t) v; + 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) v; + 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(v); + double sign = v / 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); + } + return s; +} +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); + + dc.DrawLine(0, orgy, w.GetScrX(), 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.0f %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(); + + 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.GetMultiLineTextExtent(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.GetMultiLineTextExtent(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); + + // Draw line + dc.DrawLine(orgx, 0, orgx, extend); + + // 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")) { + 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_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, _("Center"), + _("Center plot view to this position")); + m_popmenu.Append(mpID_FIT, _("Fit"), _("Set plot view to show all items")); + m_popmenu.Append(mpID_ZOOM_IN, _("Zoom in"), _("Zoom in plot view.")); + m_popmenu.Append(mpID_ZOOM_OUT, _("Zoom out"), _("Zoom out plot view.")); + m_popmenu.AppendCheckItem(mpID_LOCKASPECT, _("Lock aspect"), + _("Lock horizontal and vertical zoom aspect.")); + m_popmenu.Append(mpID_HELP_MOUSE, _("Show mouse commands..."), + _("Show help about the mouse commands.")); + + m_layers.clear(); + //m_bgColour = *wxWHITE; + m_bgColour = *wxLIGHT_GREY; + SetBackgroundColour(m_bgColour); + + m_fgColour = *wxBLACK; + + m_enableScrollBars = false; + SetSizeHints(128, 128); + + SetBackgroundStyle(wxBG_STYLE_PAINT); + + 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); + } 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 + } + 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); + } + } */ + } + 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 ¢erPoint) { + 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 ¢erPoint) { + 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(_("Supported Mouse commands:\n \ + - Left button down + Mark area: Rectangular zoom\n \ + - Right button down + Move: Pan (Move)\n \ + - Wheel: Vertical scroll\n \ + - Wheel + SHIFT: Horizontal scroll\n \ + - Wheel + CTRL: Zoom in/out"), + _("wxMathPlot help"), 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)) { + wxAutoBufferedPaintDC 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 + + wxBrush brush(GetBackgroundColour()); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(brush); + dc.SetTextForeground(m_fgColour); + dc.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(dc, *this); + }; + + // 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); + } +} + +// 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, bool copyboard) { + 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(); + if (copyboard) { + // wxBitmap bitmap(image.ConvertToBitmap()); + bool rez = false; + if (wxTheClipboard->Open()) { + // Data objects are held by the clipboard, + // so do not delete them in the app. + wxBitmap bitmap(screenImage,-1); + + wxTheClipboard->SetData(new wxBitmapDataObject(bitmap)); + wxTheClipboard->Close(); + rez = true; + } + return rez; + } + + 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); + } + } +} + +// 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; + m_flags = mpALIGN_CENTER; +} + +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(); +} +double mpFXYVector::GetDistance(double &x,double &y) { + std::vector::const_iterator itX; + std::vector::const_iterator itY; + double x1,y1; + double tx=x,ty=y; + double minD=1e38; + bool first=true; + for (itX = m_xs.begin(),itY = m_ys.begin(); itX != m_xs.end(); ++itX,++itY) { + x1=*itX; + y1=*itY; + double d=sqrt((x1-tx)*(x1-tx)+(y1-ty)*(y1-ty)); + if (first) {minD=d; x=x1;y=y1;}else + if (minD>d) {minD=d;x=x1;y=y1;} + first=false; + } + return minD; + +} + +void mpFXYVector::SetData(const std::vector &xs, + const std::vector &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::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; + } + m_minX -= 0.5f; + m_minY -= 0.5f; + m_maxX += 0.5f; + m_maxY += 0.5f; + } 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) + : drawn(false), + plotWindow(drawWindow), + wxPrintout(title) { } + +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 + for (unsigned int li = 0; li < plotWindow->CountAllLayers(); ++li) { + mpLayer *layer; + 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::iterator itXi, itXo; + std::vector::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::iterator itX = m_trans_shape_xs.begin(); + std::vector::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 &points_xs, + const std::vector &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); + } +} diff --git a/include/ctl/ctlSQLGrid.h b/include/ctl/ctlSQLGrid.h index 0faccee..f0351db 100644 --- a/include/ctl/ctlSQLGrid.h +++ b/include/ctl/ctlSQLGrid.h @@ -45,6 +45,7 @@ public: isSort=flag; }; + wxString GetColumnName(int colNum); wxSize GetBestSize(int row, int col); void OnLabelDoubleClick(wxGridEvent &event); void OnLabelClick(wxGridEvent &event); @@ -67,7 +68,6 @@ private: void OnMouseWheel(wxMouseEvent &event); void OnGridColSize(wxGridSizeEvent &event); void DrawColLabel( wxDC& dc, int col ); - wxString GetColumnName(int colNum); wxString GetColKeyValue(int col); void AppendColumnHeader(wxString &str, int start, int end); void AppendColumnHeader(wxString &str, wxArrayInt columns); diff --git a/include/ctl/ctlSQLResult.h b/include/ctl/ctlSQLResult.h index 3e5796c..9383137 100644 --- a/include/ctl/ctlSQLResult.h +++ b/include/ctl/ctlSQLResult.h @@ -19,6 +19,7 @@ #include "db/pgConn.h" #include "ctlSQLGrid.h" #include "frm/frmExport.h" +#include "frm/frmQuery.h" #define CTLSQL_RUNNING 100 // must be greater than ExecStatusType PGRES_xxx values @@ -47,6 +48,7 @@ public: wxString OnGetItemText(long item, long col) const; wxString SummaryColumn(); + wxString AutoColsPlot(int flags,frmQuery *parent); wxString CheckSelColumnDate(); void ClearFilter(); bool IsColText(int col); diff --git a/include/frm/frmPlot.h b/include/frm/frmPlot.h new file mode 100644 index 0000000..891a72c --- /dev/null +++ b/include/frm/frmPlot.h @@ -0,0 +1,48 @@ +#pragma once + +#ifndef __FRMPLOT_H +#define __FRMPLOT_H +// wxWindows headers +#include "pgAdmin3.h" +#include "frmQuery.h" +#include "mathplot.h" + +class frmPlot : public pgFrame +{ +public: + frmPlot(frmQuery* parent, const wxString& _title); + ~frmPlot(); + void Go(); + void AddSeries(const wxString legend, const std::vector& x, const std::vector& y); + void ClearAndSetAxis(wxString XtextAxis, unsigned int X_labelType, wxString YtextAxis); + void OnAbout(wxCommandEvent& event); + void OnQuit(wxCommandEvent& event); + void OnPrintPreview(wxCommandEvent& event); + void OnPrint(wxCommandEvent& event); + void OnFit(wxCommandEvent& event); + void OnAlignXAxis(wxCommandEvent& event); + void OnAlignYAxis(wxCommandEvent& event); + void OnToggleGrid(wxCommandEvent& event); + void OnCopy(wxCommandEvent& event); + void OnToggleInfoLayer(wxCommandEvent& event); + void OnSaveScreenshot(wxCommandEvent& event); + void OnToggleLissajoux(wxCommandEvent& event); + void OnToggleShowSimbol(wxCommandEvent& event); + void OnToggleCosine(wxCommandEvent& event); + void OnBlackTheme(wxCommandEvent& event); + + mpWindow* m_plot; + +private: + void OnClose(wxCloseEvent& event); + int axesPos[2]; + bool ticks; + mpInfoCoords* nfo; // mpInfoLayer* nfo; + //DECLARE_DYNAMIC_CLASS(frmPlot) + int indexColor; + DECLARE_EVENT_TABLE() +}; + + + +#endif \ No newline at end of file diff --git a/include/frm/frmQuery.h b/include/frm/frmQuery.h index 9c93c1f..04d91bd 100644 --- a/include/frm/frmQuery.h +++ b/include/frm/frmQuery.h @@ -219,6 +219,7 @@ private: void OnCopy_WhereList(wxCommandEvent& event); void OnClear_Filter(wxCommandEvent &event); void OnCheck_Column_Date(wxCommandEvent& ev); + void OnAutoColsPlot(wxCommandEvent& ev); void OnSearchReplace(wxCommandEvent &event); void OnUndo(wxCommandEvent &event); void OnRedo(wxCommandEvent &event); diff --git a/include/frm/mathplot.h b/include/frm/mathplot.h new file mode 100644 index 0000000..fd280c7 --- /dev/null +++ b/include/frm/mathplot.h @@ -0,0 +1,1875 @@ +///////////////////////////////////////////////////////////////////////////// +// 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: 22/02/2009 +// Copyright: (c) David Schalig, Davide Rondini +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _MP_MATHPLOT_H_ +#define _MP_MATHPLOT_H_ + +/** @file mathplot.h */ +/** @mainpage wxMathPlot + wxMathPlot is a framework for mathematical graph plotting in wxWindows. + + The framework is designed for convenience and ease of use. + + @section screenshots Screenshots + Go to the + screenshots page. + + @section overview Overview + The heart of wxMathPlot is mpWindow, which is a 2D canvas for plot layers. + mpWindow can be embedded as subwindow in a wxPane, a wxFrame, or any other + wxWindow. + mpWindow provides a zoomable and moveable view of the layers. The current + view can + be controlled with the mouse, the scrollbars, and a context menu. + + Plot layers are implementations of the abstract base class mpLayer. Those + can + be function plots, scale rulers, or any other vector data visualisation. + wxMathPlot provides two mpLayer implementations for plotting horizontal and + vertical rulers: mpScaleX and mpScaleY. + For convenient function plotting a series of classes derived from mpLayer + are provided, like mpFX, mpProfile, mpLegend and so on. These base classes + already come with plot code, user's own functions can be implemented by + overriding just one member for retrieving a function value. + + mpWindow has built-in support for mouse-based pan and zoom through intuitive + combinations of buttons and the mouse wheel. It also incorporates an optional + double buffering mechanism to avoid flicker. Plots can be easily sent to + printer evices or exported in bitmap formats like PNG, BMP or JPEG. + + @section coding Coding conventions + wxMathPlot sticks to wxWindow's coding conventions. All entities defined by + wxMathPlot have the prefix mp. + + @section author Author and license + wxMathPlot is published under the terms of the wxWindow license.
+ The original author is David Schalig .
+ From June 2007 the project is maintained by Davide Rondini + .
+ Authors can be contacted via the wxMathPlot's homepage at + https://sourceforge.net/projects/wxmathplot
+ Contributors:
+ Jose Luis Blanco, Val Greene.
+*/ + +// this definition uses windows dll to export function. +// WXDLLIMPEXP_MATHPLOT definition definition changed to WXDLLIMPEXP_MATHPLOT +// mathplot_EXPORTS will be defined by cmake +#ifdef mathplot_EXPORTS +#define WXDLLIMPEXP_MATHPLOT WXEXPORT +#define WXDLLIMPEXP_DATA_MATHPLOT(type) WXEXPORT type +#else // not making DLL +#define WXDLLIMPEXP_MATHPLOT +#define WXDLLIMPEXP_DATA_MATHPLOT(type) type +#endif + +#include + +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// For memory leak debug +#ifdef _WINDOWS +#ifdef _DEBUG +#include +#define DEBUG_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__) +#else +#define DEBUG_NEW new +#endif // _DEBUG +#endif // _WINDOWS + +// Separation for axes when set close to border +#define X_BORDER_SEPARATION 40 +#define Y_BORDER_SEPARATION 60 + +//----------------------------------------------------------------------------- +// classes +//----------------------------------------------------------------------------- + +class WXDLLIMPEXP_MATHPLOT mpLayer; +class WXDLLIMPEXP_MATHPLOT mpFX; +class WXDLLIMPEXP_MATHPLOT mpFY; +class WXDLLIMPEXP_MATHPLOT mpFXY; +class WXDLLIMPEXP_MATHPLOT mpFXYVector; +class WXDLLIMPEXP_MATHPLOT mpScaleX; +class WXDLLIMPEXP_MATHPLOT mpScaleY; +class WXDLLIMPEXP_MATHPLOT mpWindow; +class WXDLLIMPEXP_MATHPLOT mpText; +class WXDLLIMPEXP_MATHPLOT mpPrintout; + +/** Command IDs used by mpWindow */ +enum { + mpID_FIT = 2000, //!< Fit view to match bounding box of all layers + mpID_ZOOM_IN, //!< Zoom into view at clickposition / window center + mpID_ZOOM_OUT, //!< Zoom out + mpID_CENTER, //!< Center view on click position + mpID_LOCKASPECT, //!< Lock x/y scaling aspect + mpID_HELP_MOUSE //!< Shows information about the mouse commands +}; + +//----------------------------------------------------------------------------- +// mpLayer +//----------------------------------------------------------------------------- + +typedef enum __mp_Layer_Type { + mpLAYER_UNDEF, //!< Layer type undefined + mpLAYER_AXIS, //!< Axis type layer + mpLAYER_PLOT, //!< Plot type layer + mpLAYER_INFO, //!< Info box type layer + mpLAYER_BITMAP //!< Bitmap type layer +} mpLayerType; + +/** Plot layer, abstract base class. + Any number of mpLayer implementations can be attached to mpWindow. + Examples for mpLayer implementations are function graphs, or scale rulers. + + For convenience mpLayer defines a name, a font (wxFont), a pen (wxPen), + and a continuity property (bool) as class members. + The default values at constructor are the default font, a black pen, and + continuity set to false (draw separate points). + These may or may not be used by implementations. +*/ +class WXDLLIMPEXP_MATHPLOT mpLayer : public wxObject { +public: + mpLayer(); + + virtual ~mpLayer(){}; + + /** Check whether this layer has a bounding box. + The default implementation returns \a TRUE. Override and return + FALSE if your mpLayer implementation should be ignored by the calculation + of the global bounding box for all layers in a mpWindow. + @retval TRUE Has bounding box + @retval FALSE Has not bounding box + */ + virtual bool HasBBox() { return TRUE; } + + /** Check whether the layer is an info box. + The default implementation returns \a FALSE. It is overrided to \a TRUE + for mpInfoLayer + class and its derivative. It is necessary to define mouse actions + behaviour over + info boxes. + @return whether the layer is an info boxes + @sa mpInfoLayer::IsInfo */ + virtual bool IsInfo() { return false; }; + + /** Get inclusive left border of bounding box. + @return Value + */ + virtual double GetMinX() { return -1.0; } + + /** Get inclusive right border of bounding box. + @return Value + */ + virtual double GetMaxX() { return 1.0; } + + /** Get inclusive bottom border of bounding box. + @return Value + */ + virtual double GetMinY() { return -1.0; } + + /** Get inclusive top border of bounding box. + @return Value + */ + virtual double GetMaxY() { return 1.0; } + + /** Plot given view of layer to the given device context. + An implementation of this function has to transform layer coordinates to + wxDC coordinates based on the view parameters retrievable from the + mpWindow + passed in \a w. + Note that the public methods of mpWindow: x2p,y2p and p2x,p2y are already + provided + which transform layer coordinates to DC pixel coordinates, and user + code should rely + on them for portability and future changes to be applied + transparently, instead of + implementing the following formulas manually. + + The passed device context \a dc has its coordinate origin set to the + top-left corner + of the visible area (the default). The coordinate orientation is as shown + in the + following picture: +
+      (wxDC origin 0,0)
+             x-------------> ascending X ----------------+
+             |                                           |
+             |                                           |
+             V ascending Y                               |
+                 |                                           |
+                 |                                           |
+                 |                                           |
+                 +-------------------------------------------+  <-- right-bottom
+     corner of the mpWindow visible area.
+      
+ Note that Y ascends in downward direction, whereas the usual vertical + orientation + for mathematical plots is vice versa. Thus Y-orientation will be swapped + usually, + when transforming between wxDC and mpLayer coordinates. This change of + coordinates + is taken into account in the methods p2x,p2y,x2p,y2p. + + Rules for transformation between mpLayer and wxDC coordinates + @code + dc_X = (layer_X - mpWindow::GetPosX()) * mpWindow::GetScaleX() + dc_Y = (mpWindow::GetPosY() - layer_Y) * mpWindow::GetScaleY() // swapping + Y-orientation + + layer_X = (dc_X / mpWindow::GetScaleX()) + mpWindow::GetPosX() // scale + guaranteed to be not 0 + layer_Y = mpWindow::GetPosY() - (dc_Y / mpWindow::GetScaleY()) // swapping + Y-orientation + @endcode + + @param dc Device context to plot to. + @param w View to plot. The visible area can be retrieved from this + object. + @sa mpWindow::p2x,mpWindow::p2y,mpWindow::x2p,mpWindow::y2p + */ + virtual void Plot(wxDC &dc, mpWindow &w) = 0; + + /** Get layer name. + @return Name + */ + wxString GetName() const { return m_name; } + + /** Get font set for this layer. + @return Font + */ + const wxFont &GetFont() const { return m_font; } + + /** Get pen set for this layer. + @return Pen + */ + const wxPen &GetPen() const { return m_pen; } + + /** Set the 'continuity' property of the layer (true:draws a continuous line, + * false:draws separate points). + * @sa GetContinuity + */ + void SetContinuity(bool continuity) { m_continuous = continuity; } + + /** Gets the 'continuity' property of the layer. + * @sa SetContinuity + */ + bool GetContinuity() const { return m_continuous; } + + /** Shows or hides the text label with the name of the layer (default is + * visible). + */ + void ShowName(bool show) { m_showName = show; }; + + /** Set layer name + @param name Name, will be copied to internal class member + */ + void SetName(wxString name) { m_name = name; } + + /** Set layer font + @param font Font, will be copied to internal class member + */ + void SetFont(const wxFont &font) { m_font = font; } + + /** Set layer pen + @param pen Pen, will be copied to internal class member + */ + void SetPen(const wxPen &pen) { m_pen = pen; } + + /** Set Draw mode: inside or outside margins. Default is outside, which allows + the layer to draw up to the mpWindow border. + @param drawModeOutside The draw mode to be set */ + void SetDrawOutsideMargins(bool drawModeOutside) { + m_drawOutsideMargins = drawModeOutside; + }; + + /** Get Draw mode: inside or outside margins. + @return The draw mode */ + bool GetDrawOutsideMargins() { return m_drawOutsideMargins; }; + + /** Get a small square bitmap filled with the colour of the pen used in the + layer. Useful to create legends or similar reference to the layers. + @param side side length in pixels + @return a wxBitmap filled with layer's colour */ + wxBitmap GetColourSquare(int side = 16); + + /** Get layer type: a Layer can be of different types: plot lines, axis, info + boxes, etc, this method returns the right value. + @return An integer indicating layer type */ + mpLayerType GetLayerType() { return m_type; }; + + /** Checks whether the layer is visible or not. + @return \a true if visible */ + bool IsVisible() { return m_visible; }; + bool IsSelect() { return m_select; }; + void SetSelect(bool select) { m_select = select; }; + /** Sets layer visibility. + @param show visibility bool. */ + void SetVisible(bool show) { m_visible = show; }; + + /** Get brush set for this layer. + @return brush. */ + const wxBrush &GetBrush() const { return m_brush; }; + + /** Set layer brush + @param brush brush, will be copied to internal class member */ + void SetBrush(wxBrush brush) { m_brush = brush; }; + +protected: + wxFont m_font; //!< Layer's font + wxPen m_pen; //!< Layer's pen + wxBrush m_brush; //!< Layer's brush + wxString m_name; //!< Layer's name + bool m_continuous; //!< Specify if the layer will be plotted as a continuous + //line or a set of points. + bool m_showName; //!< States whether the name of the layer must be shown + //(default is true). + bool m_drawOutsideMargins; //!< select if the layer should draw only inside + //margins or over all DC + mpLayerType m_type; //!< Define layer type, which is assigned by constructor + bool m_visible; //!< Toggles layer visibility + bool m_select; + DECLARE_DYNAMIC_CLASS(mpLayer) +}; + +//----------------------------------------------------------------------------- +// mpInfoLayer +//----------------------------------------------------------------------------- + +/** @class mpInfoLayer + @brief Base class to create small rectangular info boxes + mpInfoLayer is the base class to create a small rectangular info box in + transparent overlay over plot layers. It is used to implement objects like + legends. +*/ +class WXDLLIMPEXP_MATHPLOT mpInfoLayer : public mpLayer { +public: + /** Default constructor. */ + mpInfoLayer(); + + /** Complete constructor. + @param rect Sets the initial size rectangle of the layer. + @param brush pointer to a fill brush. Default is transparent */ + mpInfoLayer(wxRect rect, const wxBrush *brush = wxTRANSPARENT_BRUSH); + + /** Destructor */ + virtual ~mpInfoLayer(); + + /** Updates the content of the info box. Should be overidden by derived + classes. + Update may behave in different ways according to the type of event which + called it. + @param w parent mpWindow from which to obtain informations + @param event The event which called the update. */ + virtual void UpdateInfo(mpWindow &w, wxEvent &event); + + /** mpInfoLayer has not bounding box. @sa mpLayer::HasBBox + @return always \a FALSE */ + virtual bool HasBBox() { return false; }; + + /** Plot method. Can be overidden by derived classes. + @param dc the device content where to plot + @param w the window to plot + @sa mpLayer::Plot */ + virtual void Plot(wxDC &dc, mpWindow &w); + + /** Specifies that this is an Info box layer. + @return always \a TRUE + @sa mpLayer::IsInfo */ + virtual bool IsInfo() { return true; }; + + /** Checks whether a point is inside the info box rectangle. + @param point The point to be checked + @return \a true if the point is inside the bounding box */ + virtual bool Inside(wxPoint &point); + + /** Moves the layer rectangle of given pixel deltas. + @param delta The wxPoint container for delta coordinates along x and y. + Units are in pixels. */ + virtual void Move(wxPoint delta); + + /** Updates the rectangle reference point. Used by internal methods of + * mpWindow to correctly move mpInfoLayers. */ + virtual void UpdateReference(); + + /** Returns the position of the upper left corner of the box (in pixels) + @return The rectangle position */ + wxPoint GetPosition(); + + /** Returns the size of the box (in pixels) + @return The rectangle size */ + wxSize GetSize(); + + /** Returns the current rectangle coordinates. + @return The info layer rectangle */ + const wxRect &GetRectangle() { return m_dim; }; + +protected: + wxRect m_dim; //!< The bounding rectangle of the box. It may be resized + //dynamically by the Plot method. + wxPoint m_reference; //!< Holds the reference point for movements + int m_winX, m_winY; //!< Holds the mpWindow size. Used to rescale position + //when window is resized. + + DECLARE_DYNAMIC_CLASS(mpInfoLayer) +}; + +/** @class mpInfoCoords + @brief Implements an overlay box which shows the mouse coordinates in plot + units. + When an mpInfoCoords layer is activated, when mouse is moved over the + mpWindow, its coordinates (in mpWindow units, not pixels) are continuously + reported inside the layer box. */ +class WXDLLIMPEXP_MATHPLOT mpInfoCoords : public mpInfoLayer { +public: + /** Default constructor */ + mpInfoCoords(); + /** Complete constructor, setting initial rectangle and background brush. + @param rect The initial bounding rectangle. + @param brush The wxBrush to be used for box background: default is + transparent */ + mpInfoCoords(wxRect rect, const wxBrush *brush = wxTRANSPARENT_BRUSH); + + /** Default destructor */ + ~mpInfoCoords(); + + /** Updates the content of the info box. It is used to update coordinates. + @param w parent mpWindow from which to obtain information + @param event The event which called the update. */ + virtual void UpdateInfo(mpWindow &w, wxEvent &event); + + /** Plot method. + @param dc the device content where to plot + @param w the window to plot + @sa mpLayer::Plot */ + virtual void Plot(wxDC &dc, mpWindow &w); + +protected: + wxString m_content; //!< string holding the coordinates to be drawn. + double lastx, lasty; +}; + +/** @class mpInfoLegend + @brief Implements the legend to be added to the plot + This layer allows you to add a legend to describe the plots in the window. + The legend uses the layer name as a label, and displays only layers of type + mpLAYER_PLOT. */ +class WXDLLIMPEXP_MATHPLOT mpInfoLegend : public mpInfoLayer { +public: + /** Default constructor */ + mpInfoLegend(); + + /** Complete constructor, setting initial rectangle and background brush. + @param rect The initial bounding rectangle. + @param brush The wxBrush to be used for box background: default is + transparent + @sa mpInfoLayer::mpInfoLayer */ + mpInfoLegend(wxRect rect, const wxBrush *brush = wxTRANSPARENT_BRUSH); + + /** Default destructor */ + ~mpInfoLegend(); + + /** Updates the content of the info box. Unused in this class. + @param w parent mpWindow from which to obtain information + @param event The event which called the update. */ + virtual void UpdateInfo(mpWindow &w, wxEvent &event); + + /** Plot method. + @param dc the device content where to plot + @param w the window to plot + @sa mpLayer::Plot */ + virtual void Plot(wxDC &dc, mpWindow &w); + +protected: + wxPoint m_mouse_point; +}; + +//----------------------------------------------------------------------------- +// mpLayer implementations - functions +//----------------------------------------------------------------------------- + +/** @name Label alignment constants +@{*/ + +/** @internal */ +#define mpALIGNMASK 0x03 +/** Aligns label to the right. For use with mpFX. */ +#define mpALIGN_RIGHT 0x00 +/** Aligns label to the center. For use with mpFX and mpFY. */ +#define mpALIGN_CENTER 0x01 +/** Aligns label to the left. For use with mpFX. */ +#define mpALIGN_LEFT 0x02 +/** Aligns label to the top. For use with mpFY. */ +#define mpALIGN_TOP mpALIGN_RIGHT +/** Aligns label to the bottom. For use with mpFY. */ +#define mpALIGN_BOTTOM mpALIGN_LEFT +/** Aligns X axis to bottom border. For mpScaleX */ +#define mpALIGN_BORDER_BOTTOM 0x04 +/** Aligns X axis to top border. For mpScaleX */ +#define mpALIGN_BORDER_TOP 0x05 +/** Set label for X axis in normal mode */ +#define mpX_NORMAL 0x00 +/** Set label for X axis in time mode: the value is represented as + * minutes:seconds.milliseconds if time is less than 2 minutes, + * hours:minutes:seconds otherwise. */ +#define mpX_TIME 0x01 +/** Set label for X axis in hours mode: the value is always represented as + * hours:minutes:seconds. */ +#define mpX_HOURS 0x02 +/** Set label for X axis in date mode: the value is always represented as + * yyyy-mm-dd. */ +#define mpX_DATE 0x03 +/** Set label for X axis in datetime mode: the value is always represented as + * yyyy-mm-ddThh:mm:ss. */ +#define mpX_DATETIME 0x04 +/** Aligns Y axis to left border. For mpScaleY */ +#define mpALIGN_BORDER_LEFT mpALIGN_BORDER_BOTTOM +/** Aligns Y axis to right border. For mpScaleY */ +#define mpALIGN_BORDER_RIGHT mpALIGN_BORDER_TOP +/** Aligns label to north-east. For use with mpFXY. */ +#define mpALIGN_NE 0x00 +/** Aligns label to north-west. For use with mpFXY. */ +#define mpALIGN_NW 0x01 +/** Aligns label to south-west. For use with mpFXY. */ +#define mpALIGN_SW 0x02 +/** Aligns label to south-east. For use with mpFXY. */ +#define mpALIGN_SE 0x03 + +/*@}*/ + +/** @name mpLayer implementations - functions +@{*/ + +/** Abstract base class providing plot and labeling functionality for functions + F:X->Y. + Override mpFX::GetY to implement a function. + Optionally implement a constructor and pass a name (label) and a label + alignment + to the constructor mpFX::mpFX. If the layer name is empty, no label will be + plotted. +*/ +class WXDLLIMPEXP_MATHPLOT mpFX : public mpLayer { +public: + /** @param name Label + @param flags Label alignment, pass one of #mpALIGN_RIGHT, #mpALIGN_CENTER, + #mpALIGN_LEFT. + */ + mpFX(wxString name = wxEmptyString, int flags = mpALIGN_RIGHT); + + /** Get function value for argument. + Override this function in your implementation. + @param x Argument + @return Function value + */ + virtual double GetY(double x) = 0; + + /** Layer plot handler. + This implementation will plot the function in the visible area and + put a label according to the aligment specified. + */ + virtual void Plot(wxDC &dc, mpWindow &w); + +protected: + int m_flags; //!< Holds label alignment + + DECLARE_DYNAMIC_CLASS(mpFX) +}; + +/** Abstract base class providing plot and labeling functionality for functions + F:Y->X. + Override mpFY::GetX to implement a function. + Optionally implement a constructor and pass a name (label) and a label + alignment + to the constructor mpFY::mpFY. If the layer name is empty, no label will be + plotted. +*/ +class WXDLLIMPEXP_MATHPLOT mpFY : public mpLayer { +public: + /** @param name Label + @param flags Label alignment, pass one of #mpALIGN_BOTTOM, + #mpALIGN_CENTER, #mpALIGN_TOP. + */ + mpFY(wxString name = wxEmptyString, int flags = mpALIGN_TOP); + + /** Get function value for argument. + Override this function in your implementation. + @param y Argument + @return Function value + */ + virtual double GetX(double y) = 0; + + /** Layer plot handler. + This implementation will plot the function in the visible area and + put a label according to the aligment specified. + */ + virtual void Plot(wxDC &dc, mpWindow &w); + +protected: + int m_flags; //!< Holds label alignment + + DECLARE_DYNAMIC_CLASS(mpFY) +}; + +/** Abstract base class providing plot and labeling functionality for a locus + plot F:N->X,Y. + Locus argument N is assumed to be in range 0 .. MAX_N, and implicitly + derived by enumerating + all locus values. Override mpFXY::Rewind and mpFXY::GetNextXY to implement a + locus. + Optionally implement a constructor and pass a name (label) and a label + alignment + to the constructor mpFXY::mpFXY. If the layer name is empty, no label will + be plotted. +*/ +class WXDLLIMPEXP_MATHPLOT mpFXY : public mpLayer { +public: + /** @param name Label + @param flags Label alignment, pass one of #mpALIGN_NE, #mpALIGN_NW, + #mpALIGN_SW, #mpALIGN_SE. + */ + mpFXY(wxString name = wxEmptyString, int flags = mpALIGN_NE); + + /** Rewind value enumeration with mpFXY::GetNextXY. + Override this function in your implementation. + */ + virtual void Rewind() = 0; + + /** Get locus value for next N. + Override this function in your implementation. + @param x Returns X value + @param y Returns Y value + */ + virtual bool GetNextXY(double &x, double &y) = 0; + + /** Layer plot handler. + This implementation will plot the locus in the visible area and + put a label according to the alignment specified. + */ + virtual void Plot(wxDC &dc, mpWindow &w); + /* + Show simbol point + */ + virtual void ShowSimbol(bool isShow) { m_simbol=isShow;}; +protected: + int m_flags; //!< Holds label alignment + bool m_simbol; + // Data to calculate label positioning + wxCoord maxDrawX, minDrawX, maxDrawY, minDrawY; + // int drawnPoints; + + /** Update label positioning data + @param xnew New x coordinate + @param ynew New y coordinate + */ + void UpdateViewBoundary(wxCoord xnew, wxCoord ynew); + + DECLARE_DYNAMIC_CLASS(mpFXY) +}; + +/** Abstract base class providing plot and labeling functionality for functions + F:Y->X. + Override mpProfile::GetX to implement a function. + This class is similar to mpFY, but the Plot method is different. The plot is + in fact represented by lines instead of points, which gives best rendering of + rapidly-varying functions, and in general, data which are not so close one to + another. + Optionally implement a constructor and pass a name (label) and a label + alignment + to the constructor mpProfile::mpProfile. If the layer name is empty, no + label will be plotted. +*/ +class WXDLLIMPEXP_MATHPLOT mpProfile : public mpLayer { +public: + /** @param name Label + @param flags Label alignment, pass one of #mpALIGN_BOTTOM, + #mpALIGN_CENTER, #mpALIGN_TOP. + */ + mpProfile(wxString name = wxEmptyString, int flags = mpALIGN_TOP); + + /** Get function value for argument. + Override this function in your implementation. + @param x Argument + @return Function value + */ + virtual double GetY(double x) = 0; + + /** Layer plot handler. + This implementation will plot the function in the visible area and + put a label according to the aligment specified. + */ + virtual void Plot(wxDC &dc, mpWindow &w); + +protected: + int m_flags; //!< Holds label alignment + + DECLARE_DYNAMIC_CLASS(mpProfile) +}; + +/*@}*/ + +//----------------------------------------------------------------------------- +// mpLayer implementations - furniture (scales, ...) +//----------------------------------------------------------------------------- + +/** @name mpLayer implementations - furniture (scales, ...) +@{*/ + +/** Plot layer implementing a x-scale ruler. + The ruler is fixed at Y=0 in the coordinate system. A label is plotted at + the bottom-right hand of the ruler. The scale numbering automatically + adjusts to view and zoom factor. +*/ +class WXDLLIMPEXP_MATHPLOT mpScaleX : public mpLayer { +public: + /** Full constructor. + @param name Label to plot by the ruler + @param flags Set the position of the scale with respect to the + window. + @param ticks Select ticks or grid. Give TRUE (default) for drawing + axis ticks, FALSE for drawing the grid. + @param type mpX_NORMAL for normal labels, mpX_TIME for time axis + in hours, minutes, seconds. */ + mpScaleX(wxString name = wxT("X"), int flags = mpALIGN_CENTER, + bool ticks = true, unsigned int type = mpX_NORMAL); + + /** Layer plot handler. + This implementation will plot the ruler adjusted to the visible area. */ + virtual void Plot(wxDC &dc, mpWindow &w); + // Get text label for value + virtual wxString GetLabelTextValue(double &v); + /** Check whether this layer has a bounding box. + This implementation returns \a FALSE thus making the ruler invisible + to the plot layer bounding box calculation by mpWindow. */ + virtual bool HasBBox() { return FALSE; } + + /** Set X axis alignment. + @param align alignment (choose between mpALIGN_BORDER_BOTTOM, + mpALIGN_BOTTOM, mpALIGN_CENTER, mpALIGN_TOP, mpALIGN_BORDER_TOP */ + void SetAlign(int align) { m_flags = align; }; + + /** Set X axis ticks or grid + @param ticks TRUE to plot axis ticks, FALSE to plot grid. */ + void SetTicks(bool ticks) { m_ticks = ticks; }; + + /** Get X axis ticks or grid + @return TRUE if plot is drawing axis ticks, FALSE if the grid is active. + */ + bool GetTicks() { return m_ticks; }; + + /** Get X axis label view mode. + @return mpX_NORMAL for normal labels, mpX_TIME for time axis in hours, + minutes, seconds. */ + unsigned int GetLabelMode() { return m_labelType; }; + + /** Set X axis label view mode. + @param mode mpX_NORMAL for normal labels, mpX_TIME for time axis in hours, + minutes, seconds. */ + void SetLabelMode(unsigned int mode) { m_labelType = mode; }; + + /** Set X axis Label format (used for mpX_NORMAL draw mode). + @param format The format string */ + void SetLabelFormat(const wxString &format) { m_labelFormat = format; }; + + /** Get X axis Label format (used for mpX_NORMAL draw mode). + @return The format string */ + const wxString &SetLabelFormat() { return m_labelFormat; }; + +protected: + int m_flags; //!< Flag for axis alignment + bool m_ticks; //!< Flag to toggle between ticks or grid + unsigned int m_labelType; //!< Select labels mode: mpX_NORMAL for normal + //labels, mpX_TIME for time axis in hours, minutes, + //seconds + wxString m_labelFormat; //!< Format string used to print labels + + DECLARE_DYNAMIC_CLASS(mpScaleX) +}; + +/** Plot layer implementing a y-scale ruler. + If align is set to mpALIGN_CENTER, the ruler is fixed at X=0 in the + coordinate system. If the align is set to mpALIGN_TOP or mpALIGN_BOTTOM, the + axis is always drawn respectively at top or bottom of the window. A label is + plotted at + the top-right hand of the ruler. The scale numbering automatically + adjusts to view and zoom factor. +*/ +class WXDLLIMPEXP_MATHPLOT mpScaleY : public mpLayer { +public: + /** @param name Label to plot by the ruler + @param flags Set position of the scale respect to the window. + @param ticks Select ticks or grid. Give TRUE (default) for drawing axis + ticks, FALSE for drawing the grid */ + mpScaleY(wxString name = wxT("Y"), int flags = mpALIGN_CENTER, + bool ticks = true); + + /** Layer plot handler. + This implementation will plot the ruler adjusted to the visible area. + */ + virtual void Plot(wxDC &dc, mpWindow &w); + + /** Check whether this layer has a bounding box. + This implementation returns \a FALSE thus making the ruler invisible + to the plot layer bounding box calculation by mpWindow. + */ + virtual bool HasBBox() { return FALSE; } + + /** Set Y axis alignment. + @param align alignment (choose between mpALIGN_BORDER_LEFT, mpALIGN_LEFT, + mpALIGN_CENTER, mpALIGN_RIGHT, mpALIGN_BORDER_RIGHT) */ + void SetAlign(int align) { m_flags = align; }; + + /** Set Y axis ticks or grid + @param ticks TRUE to plot axis ticks, FALSE to plot grid. */ + void SetTicks(bool ticks) { m_ticks = ticks; }; + + /** Get Y axis ticks or grid + @return TRUE if plot is drawing axis ticks, FALSE if the grid is active. + */ + bool GetTicks() { return m_ticks; }; + + /** Set Y axis Label format. + @param format The format string */ + void SetLabelFormat(const wxString &format) { m_labelFormat = format; }; + + /** Get Y axis Label format. + @return The format string */ + const wxString &SetLabelFormat() { return m_labelFormat; }; + +protected: + int m_flags; //!< Flag for axis alignment + bool m_ticks; //!< Flag to toggle between ticks or grid + wxString m_labelFormat; //!< Format string used to print labels + + DECLARE_DYNAMIC_CLASS(mpScaleY) +}; + +//----------------------------------------------------------------------------- +// mpWindow +//----------------------------------------------------------------------------- + +/** @name Constants defining mouse modes for mpWindow +@{*/ + +/** Mouse panning drags the view. Mouse mode for mpWindow. */ +#define mpMOUSEMODE_DRAG 0 +/** Mouse panning creates a zoom box. Mouse mode for mpWindow. */ +#define mpMOUSEMODE_ZOOMBOX 1 + +/*@}*/ +/** Define the type for the list of layers inside mpWindow */ +// WX_DECLARE_HASH_MAP( int, mpLayer*, wxIntegerHash, wxIntegerEqual, +// wxLayerList ); +typedef std::deque wxLayerList; + +/** Canvas for plotting mpLayer implementations. + + This class defines a zoomable and moveable 2D plot canvas. Any number + of mpLayer implementations (scale rulers, function plots, ...) can be + attached using mpWindow::AddLayer. + + The canvas window provides a context menu with actions for navigating the + view. + The context menu can be retrieved with mpWindow::GetPopupMenu, e.g. for + extending it + externally. + + Since wxMathPlot version 0.03, the mpWindow incorporates the following + features: + - DoubleBuffering (Default=disabled): Can be set with EnableDoubleBuffer + - Mouse based pan/zoom (Default=enabled): Can be set with + EnableMousePanZoom. + + The mouse commands can be visualized by the user through the popup menu, and + are: + - Mouse Move+CTRL: Pan (Move) + - Mouse Wheel: Vertical scroll + - Mouse Wheel+SHIFT: Horizontal scroll + - Mouse Wheel UP+CTRL: Zoom in + - Mouse Wheel DOWN+CTRL: Zoom out + +*/ +class WXDLLIMPEXP_MATHPLOT mpWindow : public wxWindow { +public: + mpWindow() {} + mpWindow(wxWindow *parent, wxWindowID id, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, long flags = 0); + ~mpWindow(); + + /** Get reference to context menu of the plot canvas. + @return Pointer to menu. The menu can be modified. + */ + wxMenu *GetPopupMenu() { return &m_popmenu; } + + /** Add a plot layer to the canvas. + @param layer Pointer to layer. The mpLayer object will get under control + of mpWindow, + i.e. it will be delete'd on mpWindow destruction + @param refreshDisplay States whether to refresh the display (UpdateAll) + after adding the layer. + @retval TRUE Success + @retval FALSE Failure due to out of memory. + */ + bool AddLayer(mpLayer *layer, bool refreshDisplay = true); + + /** Remove a plot layer from the canvas. + @param layer Pointer to layer. The mpLayer object will be destructed using + delete. + @param alsoDeleteObject If set to true, the mpLayer object will be also + "deleted", not just removed from the internal list. + @param refreshDisplay States whether to refresh the display (UpdateAll) + after removing the layer. + @return true if layer is deleted correctly + + N.B. Only the layer reference in the mpWindow is deleted, the layer object + still exists! + */ + bool DelLayer(mpLayer *layer, bool alsoDeleteObject = false, + bool refreshDisplay = true); + + /** Remove all layers from the plot. + @param alsoDeleteObject If set to true, the mpLayer objects will be also + "deleted", not just removed from the internal list. + @param refreshDisplay States whether to refresh the display (UpdateAll) + after removing the layers. + */ + void DelAllLayers(bool alsoDeleteObject, bool refreshDisplay = true); + + /*! Get the layer in list position indicated. + N.B. You must know the index of the layer inside the list! + @param position position of the layer in the layers list + @return pointer to mpLayer + */ + mpLayer *GetLayer(int position); + + /*! Get the layer by its name (case sensitive). + @param name The name of the layer to retrieve + @return A pointer to the mpLayer object, or NULL if not found. + */ + mpLayer *GetLayerByName(const wxString &name); + + /** Get current view's X scale. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return Scale + */ + double GetXscl() { return m_scaleX; } + double GetScaleX(void) const { + return m_scaleX; + }; // Schaling's method: maybe another method esists with the same name + + /** Get current view's Y scale. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return Scale + */ + double GetYscl() const { return m_scaleY; } + double GetScaleY(void) const { + return m_scaleY; + } // Schaling's method: maybe another method exists with the same name + + /** Get current view's X position. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return X Position in layer coordinate system, that corresponds to the + center point of the view. + */ + double GetXpos() const { return m_posX; } + double GetPosX(void) const { return m_posX; } + + /** Get current view's Y position. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return Y Position in layer coordinate system, that corresponds to the + center point of the view. + */ + double GetYpos() const { return m_posY; } + double GetPosY(void) const { return m_posY; } + + /** Get current view's X dimension in device context units. + Usually this is equal to wxDC::GetSize, but it might differ thus mpLayer + implementations should rely on the value returned by the function. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return X dimension. + */ + int GetScrX(void) const { return m_scrX; } + int GetXScreen(void) const { return m_scrX; } + + /** Get current view's Y dimension in device context units. + Usually this is equal to wxDC::GetSize, but it might differ thus mpLayer + implementations should rely on the value returned by the function. + See @ref mpLayer::Plot "rules for coordinate transformation" + @return Y dimension. + */ + int GetScrY(void) const { return m_scrY; } + int GetYScreen(void) const { return m_scrY; } + + /** Set current view's X scale and refresh display. + @param scaleX New scale, must not be 0. + */ + void SetScaleX(double scaleX); + + /** Set current view's Y scale and refresh display. + @param scaleY New scale, must not be 0. + */ + void SetScaleY(double scaleY) { + if (scaleY != 0) + m_scaleY = scaleY; + UpdateAll(); + } + + /** Set current view's X position and refresh display. + @param posX New position that corresponds to the center point of the view. + */ + void SetPosX(double posX) { + m_posX = posX; + UpdateAll(); + } + + /** Set current view's Y position and refresh display. + @param posY New position that corresponds to the center point of the view. + */ + void SetPosY(double posY) { + m_posY = posY; + UpdateAll(); + } + + /** Set current view's X and Y position and refresh display. + @param posX New position that corresponds to the center point of the view. + @param posY New position that corresponds to the center point of the view. + */ + void SetPos(double posX, double posY) { + m_posX = posX; + m_posY = posY; + UpdateAll(); + } + + /** Set current view's dimensions in device context units. + Needed by plotting functions. It doesn't refresh display. + @param scrX New position that corresponds to the center point of the view. + @param scrY New position that corresponds to the center point of the view. + */ + void SetScr(int scrX, int scrY) { + m_scrX = scrX; + m_scrY = scrY; + } + + /** Converts mpWindow (screen) pixel coordinates into graph (floating point) + * coordinates, using current mpWindow position and scale. + * @sa p2y,x2p,y2p */ + // double p2x(wxCoord pixelCoordX, bool drawOutside = true ); // { return + // m_posX + pixelCoordX/m_scaleX; } + inline double p2x(wxCoord pixelCoordX) { + return m_posX + pixelCoordX / m_scaleX; + } + + /** Converts mpWindow (screen) pixel coordinates into graph (floating point) + * coordinates, using current mpWindow position and scale. + * @sa p2x,x2p,y2p */ + // double p2y(wxCoord pixelCoordY, bool drawOutside = true ); //{ return + // m_posY - pixelCoordY/m_scaleY; } + inline double p2y(wxCoord pixelCoordY) { + return m_posY - pixelCoordY / m_scaleY; + } + + /** Converts graph (floating point) coordinates into mpWindow (screen) pixel + * coordinates, using current mpWindow position and scale. + * @sa p2x,p2y,y2p */ + // wxCoord x2p(double x, bool drawOutside = true); // { return (wxCoord) ( + // (x-m_posX) * m_scaleX); } + inline wxCoord x2p(double x) { return (wxCoord)((x - m_posX) * m_scaleX); } + + /** Converts graph (floating point) coordinates into mpWindow (screen) pixel + * coordinates, using current mpWindow position and scale. + * @sa p2x,p2y,x2p */ + // wxCoord y2p(double y, bool drawOutside = true); // { return (wxCoord) ( + // (m_posY-y) * m_scaleY); } + inline wxCoord y2p(double y) { return (wxCoord)((m_posY - y) * m_scaleY); } + + /** Enable/disable the feature of pan/zoom with the mouse (default=enabled) + */ + void EnableMousePanZoom(bool enabled) { m_enableMouseNavigation = enabled; } + + /** Enable or disable X/Y scale aspect locking for the view. + @note Explicit calls to mpWindow::SetScaleX and mpWindow::SetScaleY will + set + an unlocked aspect, but any other action changing the view scale + will + lock the aspect again. + */ + void LockAspect(bool enable = TRUE); + + /** Checks whether the X/Y scale aspect is locked. + @retval TRUE Locked + @retval FALSE Unlocked + */ + inline bool IsAspectLocked() { return m_lockaspect; } + + /** Set view to fit global bounding box of all plot layers and refresh + display. + Scale and position will be set to show all attached mpLayers. + The X/Y scale aspect lock is taken into account. + */ + void Fit(); + + /** Set view to fit a given bounding box and refresh display. + The X/Y scale aspect lock is taken into account. + If provided, the parameters printSizeX and printSizeY are taken as the DC + size, and the + pixel scales are computed accordingly. Also, in this case the passed + borders are not saved + as the "desired borders", since this use will be invoked only when + printing. + */ + void Fit(double xMin, double xMax, double yMin, double yMax, + wxCoord *printSizeX = NULL, wxCoord *printSizeY = NULL); + + /** Zoom into current view and refresh display + * @param centerPoint The point (pixel coordinates) that will stay in the + * same position on the screen after the zoom (by default, the center of the + * mpWindow). + */ + void ZoomIn(const wxPoint ¢erPoint = wxDefaultPosition); + + /** Zoom out current view and refresh display + * @param centerPoint The point (pixel coordinates) that will stay in the + * same position on the screen after the zoom (by default, the center of the + * mpWindow). + */ + void ZoomOut(const wxPoint ¢erPoint = wxDefaultPosition); + + /** Zoom in current view along X and refresh display */ + void ZoomInX(); + /** Zoom out current view along X and refresh display */ + void ZoomOutX(); + /** Zoom in current view along Y and refresh display */ + void ZoomInY(); + /** Zoom out current view along Y and refresh display */ + void ZoomOutY(); + + /** Zoom view fitting given coordinates to the window (p0 and p1 do not need + * to be in any specific order) */ + void ZoomRect(wxPoint p0, wxPoint p1); + + /** Refresh display */ + void UpdateAll(); + + // Added methods by Davide Rondini + + /** Counts the number of plot layers, excluding axes or text: this is to count + only the layers which have a bounding box. + \return The number of profiles plotted. + */ + unsigned int CountLayers(); + + /** Counts the number of plot layers, whether or not they have a bounding box. + \return The number of layers in the mpWindow. */ + unsigned int CountAllLayers() { return m_layers.size(); }; + + /** Draws the mpWindow on a page for printing + \param print the mpPrintout where to print the graph */ + // void PrintGraph(mpPrintout *print); + + /** Returns the left-border layer coordinate that the user wants the mpWindow + * to show (it may be not exactly the actual shown coordinate in the case of + * locked aspect ratio). + * @sa Fit + */ + double GetDesiredXmin() { return m_desiredXmin; } + + /** Returns the right-border layer coordinate that the user wants the mpWindow + * to show (it may be not exactly the actual shown coordinate in the case of + * locked aspect ratio). + * @sa Fit + */ + double GetDesiredXmax() { return m_desiredXmax; } + + /** Returns the bottom-border layer coordinate that the user wants the + * mpWindow to show (it may be not exactly the actual shown coordinate in the + * case of locked aspect ratio). + * @sa Fit + */ + double GetDesiredYmin() { return m_desiredYmin; } + + /** Returns the top layer-border coordinate that the user wants the mpWindow + * to show (it may be not exactly the actual shown coordinate in the case of + * locked aspect ratio). + * @sa Fit + */ + double GetDesiredYmax() { return m_desiredYmax; } + + /** Returns the bounding box coordinates + @param bbox Pointer to a 6-element double array where to store + bounding box coordinates. */ + void GetBoundingBox(double *bbox); + + /** Enable/disable scrollbars + @param status Set to true to show scrollbars */ + void SetMPScrollbars(bool status); + + /** Get scrollbars status. + @return true if scrollbars are visible */ + bool GetMPScrollbars() { return m_enableScrollBars; }; + + /** Draw the window on a wxBitmap, then save it to a file. + @param filename File name where to save the screenshot + @param type image type to be saved: see wxImage output file types for flags + @param imageSize Set a size for the output image. Default is the same as + the screen size + @param fit Decide whether to fit the plot into the size*/ + bool SaveScreenshot(const wxString &filename, wxBitmapType type = wxBITMAP_TYPE_BMP, + wxSize imageSize = wxDefaultSize, bool fit = false,bool copyboard=false); + + /** This value sets the zoom steps whenever the user clicks "Zoom in/out" or + * performs zoom with the mouse wheel. + * It must be a number above unity. This number is used for zoom in, and its + * inverse for zoom out. Set to 1.5 by default. */ + static double zoomIncrementalFactor; + + /** Set window margins, creating a blank area where some kinds of layers + cannot draw. This is useful for example to draw axes outside the area where + the plots are drawn. + @param top Top border + @param right Right border + @param bottom Bottom border + @param left Left border */ + void SetMargins(int top, int right, int bottom, int left); + + /** Set the top margin. @param top Top Margin */ + void SetMarginTop(int top) { m_marginTop = top; }; + /** Set the right margin. @param right Right Margin */ + void SetMarginRight(int right) { m_marginRight = right; }; + /** Set the bottom margin. @param bottom Bottom Margin */ + void SetMarginBottom(int bottom) { m_marginBottom = bottom; }; + /** Set the left margin. @param left Left Margin */ + void SetMarginLeft(int left) { m_marginLeft = left; }; + + /** Get the top margin. @param top Top Margin */ + int GetMarginTop() { return m_marginTop; }; + /** Get the right margin. @param right Right Margin */ + int GetMarginRight() { return m_marginRight; }; + /** Get the bottom margin. @param bottom Bottom Margin */ + int GetMarginBottom() { return m_marginBottom; }; + /** Get the left margin. @param left Left Margin */ + int GetMarginLeft() { return m_marginLeft; }; + + /** Sets whether to show coordinate tooltip when mouse passes over the plot. + * \param value true for enable, false for disable */ + // void EnableCoordTooltip(bool value = true); + /** Gets coordinate tooltip status. \return true for enable, false for disable + */ + // bool GetCoordTooltip() { return m_coordTooltip; }; + + /** Check if a given point is inside the area of a mpInfoLayer and eventually + returns its pointer. + @param point The position to be checked + @return If an info layer is found, returns its pointer, NULL otherwise */ + mpInfoLayer *IsInsideInfoLayer(wxPoint &point); + + /** Sets the visibility of a layer by its name. + @param name The layer name to set visibility + @param viewable the view status to be set */ + void SetLayerVisible(const wxString &name, bool viewable); + + /** Check whether a layer with given name is visible + @param name The layer name + @return layer visibility status */ + bool IsLayerVisible(const wxString &name); + + /** Sets the visibility of a layer by its position in layer list. + @param position The layer position in layer list + @param viewable the view status to be set */ + void SetLayerVisible(const unsigned int position, bool viewable); + + /** Check whether the layer at given position is visible + @param position The layer position in layer list + @return layer visibility status */ + bool IsLayerVisible(const unsigned int position); + + /** Set Color theme. Provide colours to set a new colour theme. + @param bgColour Background colour + @param drawColour The colour used to draw all elements in foreground, + axes excluded + @param axesColour The colour used to draw axes (but not their labels)*/ + void SetColourTheme(const wxColour &bgColour, const wxColour &drawColour, + const wxColour &axesColour); + + /** Get axes draw colour + @return reference to axis colour used in theme */ + const wxColour &GetAxesColour() { return m_axColour; }; + +protected: + void OnPaint( + wxPaintEvent &event); //!< Paint handler, will plot all attached layers + void + OnSize(wxSizeEvent &event); //!< Size handler, will update scroll bar sizes + // void OnScroll2 (wxScrollWinEvent &event); //!< Scroll handler, will + // move canvas + void OnShowPopupMenu( + wxMouseEvent &event); //!< Mouse handler, will show context menu + void OnMouseRightDown(wxMouseEvent &event); //!< Mouse handler, for detecting + //when the user drags with the + //right button or just "clicks" + //for the menu + void OnCenter(wxCommandEvent &event); //!< Context menu handler + void OnFit(wxCommandEvent &event); //!< Context menu handler + void OnZoomIn(wxCommandEvent &event); //!< Context menu handler + void OnZoomOut(wxCommandEvent &event); //!< Context menu handler + void OnLockAspect(wxCommandEvent &event); //!< Context menu handler + void OnMouseHelp(wxCommandEvent &event); //!< Context menu handler + void OnMouseWheel(wxMouseEvent &event); //!< Mouse handler for the wheel + void OnMouseMove( + wxMouseEvent &event); //!< Mouse handler for mouse motion (for pan) + void + OnMouseLeftDown(wxMouseEvent &event); //!< Mouse left click (for rect zoom) + void + OnMouseLeftRelease(wxMouseEvent &event); //!< Mouse left click (for rect zoom) + void OnScrollThumbTrack( + wxScrollWinEvent &event); //!< Scroll thumb on scroll bar moving + void OnScrollPageUp(wxScrollWinEvent &event); //!< Scroll page up + void OnScrollPageDown(wxScrollWinEvent &event); //!< Scroll page down + void OnScrollLineUp(wxScrollWinEvent &event); //!< Scroll line up + void OnScrollLineDown(wxScrollWinEvent &event); //!< Scroll line down + void OnScrollTop(wxScrollWinEvent &event); //!< Scroll to top + void OnScrollBottom(wxScrollWinEvent &event); //!< Scroll to bottom + + void DoScrollCalc(const int position, const int orientation); + + void DoZoomInXCalc(const int staticXpixel); + void DoZoomInYCalc(const int staticYpixel); + void DoZoomOutXCalc(const int staticXpixel); + void DoZoomOutYCalc(const int staticYpixel); + + /** Recalculate global layer bounding box, and save it in m_minX,... + * \return true if there is any valid BBox information. + */ + virtual bool UpdateBBox(); + + // wxList m_layers; //!< List of attached plot layers + wxLayerList m_layers; //!< List of attached plot layers + wxMenu m_popmenu; //!< Canvas' context menu + bool m_lockaspect; //!< Scale aspect is locked or not + // bool m_coordTooltip; //!< Selects whether to show coordinate tooltip + wxColour m_bgColour; //!< Background Colour + wxColour m_fgColour; //!< Foreground Colour + wxColour m_axColour; //!< Axes Colour + + double m_minX; //!< Global layer bounding box, left border incl. + double m_maxX; //!< Global layer bounding box, right border incl. + double m_minY; //!< Global layer bounding box, bottom border incl. + double m_maxY; //!< Global layer bounding box, top border incl. + double m_scaleX; //!< Current view's X scale + double m_scaleY; //!< Current view's Y scale + double m_posX; //!< Current view's X position + double m_posY; //!< Current view's Y position + int m_scrX; //!< Current view's X dimension + int m_scrY; //!< Current view's Y dimension + int m_clickedX; //!< Last mouse click X position, for centering and zooming + //the view + int m_clickedY; //!< Last mouse click Y position, for centering and zooming + //the view + + /** These are updated in Fit() only, and may be different from the real + * borders (layer coordinates) only if lock aspect ratio is true. + */ + double m_desiredXmin, m_desiredXmax, m_desiredYmin, m_desiredYmax; + + int m_marginTop, m_marginRight, m_marginBottom, m_marginLeft; + + int m_last_lx, m_last_ly; //!< For double buffering + wxMemoryDC m_buff_dc; //!< For double buffering + wxBitmap *m_buff_bmp; //!< For double buffering + bool m_enableMouseNavigation; //!< For pan/zoom with the mouse. + bool m_mouseMovedAfterRightClick; + long m_mouseRClick_X, + m_mouseRClick_Y; //!< For the right button "drag" feature + int m_mouseLClick_X, + m_mouseLClick_Y; //!< Starting coords for rectangular zoom selection + bool m_enableScrollBars; + int m_scrollX, m_scrollY; + mpInfoLayer * + m_movingInfoLayer; //!< For moving info layers over the window area + + DECLARE_DYNAMIC_CLASS(mpWindow) + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// mpFXYVector - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- + +/** A class providing graphs functionality for a 2D plot (either continuous or a + set of points), from vectors of data. + This class can be used directly, the user does not need to derive any new + class. Simply pass the data as two vectors + with the same length containing the X and Y coordinates to the method + SetData. + + To generate a graph with a set of points, call + \code + layerVar->SetContinuity(false) + \endcode + + or + + \code + layerVar->SetContinuity(true) + \endcode + + to render the sequence of coordinates as a continuous line. + + (Added: Jose Luis Blanco, AGO-2007) +*/ +class WXDLLIMPEXP_MATHPLOT mpFXYVector : public mpFXY { +public: + /** @param name Label + @param flags Label alignment, pass one of #mpALIGN_NE, #mpALIGN_NW, + #mpALIGN_SW, #mpALIGN_SE. + */ + mpFXYVector(wxString name = wxEmptyString, int flags = mpALIGN_NE); + + /** Changes the internal data: the set of points to draw. + Both vectors MUST be of the same length. This method DOES NOT refresh the + mpWindow; do it manually. + * @sa Clear + */ + void SetData(const std::vector &xs, const std::vector &ys); + + /** Clears all the data, leaving the layer empty. + * @sa SetData + */ + void Clear(); + double GetDistance(double &x,double &y); +protected: + /** The internal copy of the set of data to draw. + */ + std::vector m_xs, m_ys; + + /** The internal counter for the "GetNextXY" interface + */ + size_t m_index; + + /** Loaded at SetData + */ + double m_minX, m_maxX, m_minY, m_maxY; + + /** Rewind value enumeration with mpFXY::GetNextXY. + Overridden in this implementation. + */ + void Rewind(); + + /** Get locus value for next N. + Overridden in this implementation. + @param x Returns X value + @param y Returns Y value + */ + bool GetNextXY(double &x, double &y); + + /** Returns the actual minimum X data (loaded in SetData). + */ + double GetMinX() { return m_minX; } + + /** Returns the actual minimum Y data (loaded in SetData). + */ + double GetMinY() { return m_minY; } + + /** Returns the actual maximum X data (loaded in SetData). + */ + double GetMaxX() { return m_maxX; } + + /** Returns the actual maximum Y data (loaded in SetData). + */ + double GetMaxY() { return m_maxY; } + + int m_flags; //!< Holds label alignment + + DECLARE_DYNAMIC_CLASS(mpFXYVector) +}; + +//----------------------------------------------------------------------------- +// mpText - provided by Val Greene +//----------------------------------------------------------------------------- + +/** Plot layer implementing a text string. +The text is plotted using a percentage system 0-100%, so the actual +coordinates for the location are not required, and the text stays +on the plot reguardless of the other layers location and scaling +factors. +*/ +class WXDLLIMPEXP_MATHPLOT mpText : public mpLayer { +public: + /** @param name text to be drawn in the plot + @param offsetx holds offset for the X location in percentage (0-100) + @param offsety holds offset for the Y location in percentage (0-100) */ + mpText(wxString name = wxT("Title"), int offsetx = 5, int offsety = 50); + + /** Text Layer plot handler. + This implementation will plot text adjusted to the visible area. */ + virtual void Plot(wxDC &dc, mpWindow &w); + + /** mpText should not be used for scaling decisions. */ + virtual bool HasBBox() { return FALSE; } + +protected: + int m_offsetx; //!< Holds offset for X in percentage + int m_offsety; //!< Holds offset for Y in percentage + + DECLARE_DYNAMIC_CLASS(mpText) +}; + +//----------------------------------------------------------------------------- +// mpPrintout - provided by Davide Rondini +//----------------------------------------------------------------------------- + +/** Printout class used by mpWindow to draw in the objects to be printed. + The object itself can then used by the default wxWidgets printing system + to print mppWindow objects. +*/ +class WXDLLIMPEXP_MATHPLOT mpPrintout : public wxPrintout { +public: + mpPrintout(mpWindow *drawWindow, + const wxChar *title = _T("wxMathPlot print output")); + virtual ~mpPrintout(){}; + + void SetDrawState(bool drawState) { drawn = drawState; }; + bool OnPrintPage(int page); + bool HasPage(int page); + +private: + mpPrintout() : drawn(false), plotWindow(NULL) {}; + bool drawn; + mpWindow *plotWindow; +}; + +//----------------------------------------------------------------------------- +// mpMovableObject - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +/** This virtual class represents objects that can be moved to an arbitrary 2D + * location+rotation. + * The current transformation is set through SetCoordinateBase. + * To ease the implementation of descendent classes, mpMovableObject will + * be in charge of Bounding Box computation and layer rendering, assuming that + * the object updates its shape in m_shape_xs & m_shape_ys. + */ +class WXDLLIMPEXP_MATHPLOT mpMovableObject : public mpLayer { +public: + /** Default constructor (sets location and rotation to (0,0,0)) + */ + mpMovableObject() + : m_reference_x(0), m_reference_y(0), m_reference_phi(0), m_shape_xs(0), + m_shape_ys(0) { + m_type = mpLAYER_PLOT; + } + + virtual ~mpMovableObject(){}; + + /** Get the current coordinate transformation. + */ + void GetCoordinateBase(double &x, double &y, double &phi) const { + x = m_reference_x; + y = m_reference_y; + phi = m_reference_phi; + } + + /** Set the coordinate transformation (phi in radians, 0 means no rotation). + */ + void SetCoordinateBase(double x, double y, double phi = 0) { + m_reference_x = x; + m_reference_y = y; + m_reference_phi = phi; + m_flags = mpALIGN_NE; + ShapeUpdated(); + } + + virtual bool HasBBox() { return m_trans_shape_xs.size() != 0; } + + /** Get inclusive left border of bounding box. + */ + virtual double GetMinX() { return m_bbox_min_x; } + + /** Get inclusive right border of bounding box. + */ + virtual double GetMaxX() { return m_bbox_max_x; } + + /** Get inclusive bottom border of bounding box. + */ + virtual double GetMinY() { return m_bbox_min_y; } + + /** Get inclusive top border of bounding box. + */ + virtual double GetMaxY() { return m_bbox_max_y; } + + virtual void Plot(wxDC &dc, mpWindow &w); + + /** Set label axis alignment. + * @param align alignment (choose between mpALIGN_NE, mpALIGN_NW, + * mpALIGN_SW, mpALIGN_SE + */ + void SetAlign(int align) { m_flags = align; }; + +protected: + int m_flags; //!< Holds label alignment + + /** The coordinates of the object (orientation "phi" is in radians). + */ + double m_reference_x, m_reference_y, m_reference_phi; + + /** A method for 2D translation and rotation, using the current transformation + * stored in m_reference_x,m_reference_y,m_reference_phi. + */ + void TranslatePoint(double x, double y, double &out_x, double &out_y); + + /** This contains the object points, in local coordinates (to be transformed + * by the current transformation). + */ + std::vector m_shape_xs, m_shape_ys; + + /** The buffer for the translated & rotated points (to avoid recomputing them + *with each mpWindow refresh). + * + */ + std::vector m_trans_shape_xs, m_trans_shape_ys; + + /** The precomputed bounding box: + * @sa ShapeUpdated + */ + double m_bbox_min_x, m_bbox_max_x, m_bbox_min_y, m_bbox_max_y; + + /** Must be called by the descendent class after updating the shape + * (m_shape_xs/ys), or when the transformation changes. + * This method updates the buffers m_trans_shape_xs/ys, and the precomputed + * bounding box. + */ + void ShapeUpdated(); +}; + +//----------------------------------------------------------------------------- +// mpCovarianceEllipse - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +/** A 2D ellipse, described by a 2x2 covariance matrix. + * The relation between the multivariate Gaussian confidence interval and + * the "quantiles" in this class is: + * - 1 : 68.27% confidence interval + * - 2 : 95.45% + * - 3 : 99.73% + * - 4 : 99.994% + * For example, see + *http://en.wikipedia.org/wiki/Normal_distribution#Standard_deviation_and_confidence_intervals + * + * The ellipse will be always centered at the origin. Use + *mpMovableObject::SetCoordinateBase to move it. + */ +class WXDLLIMPEXP_MATHPLOT mpCovarianceEllipse : public mpMovableObject { +public: + /** Default constructor. + * Initializes to a unity diagonal covariance matrix, a 95% confidence + * interval (2 sigmas), 32 segments, and a continuous plot + * (m_continuous=true). + */ + mpCovarianceEllipse(double cov_00 = 1, double cov_11 = 1, double cov_01 = 0, + double quantiles = 2, int segments = 32, + const wxString &layerName = wxT("")) + : m_cov_00(cov_00), m_cov_11(cov_11), m_cov_01(cov_01), + m_quantiles(quantiles), m_segments(segments) { + m_continuous = true; + m_name = layerName; + RecalculateShape(); + m_type = mpLAYER_PLOT; + } + + virtual ~mpCovarianceEllipse() {} + + double GetQuantiles() const { return m_quantiles; } + + /** Set how many "quantiles" to draw, that is, the confidence interval of the + * ellipse (see above). + */ + void SetQuantiles(double q) { + m_quantiles = q; + RecalculateShape(); + } + + void SetSegments(int segments) { m_segments = segments; } + int GetSegments() const { return m_segments; } + + /** Returns the elements of the current covariance matrix: + */ + void GetCovarianceMatrix(double &cov_00, double &cov_01, + double &cov_11) const { + cov_00 = m_cov_00; + cov_01 = m_cov_01; + cov_11 = m_cov_11; + } + + /** Changes the covariance matrix: + */ + void SetCovarianceMatrix(double cov_00, double cov_01, double cov_11) { + m_cov_00 = cov_00; + m_cov_01 = cov_01; + m_cov_11 = cov_11; + RecalculateShape(); + } + +protected: + /** The elements of the matrix (only 3 since cov(0,1)=cov(1,0) in any positive + * definite matrix). + */ + double m_cov_00, m_cov_11, m_cov_01; + double m_quantiles; + + /** The number of line segments that build up the ellipse. + */ + int m_segments; + + /** Called to update the m_shape_xs, m_shape_ys vectors, whenever a parameter + * changes. + */ + void RecalculateShape(); +}; + +//----------------------------------------------------------------------------- +// mpPolygon - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +/** An arbitrary polygon, descendant of mpMovableObject. + * Use "setPoints" to set the list of N points. This class also can draw + * non-closed polygons by + * passing the appropriate parameters to "setPoints". To draw a point-cloud, + * call "SetContinuity(false)". + */ +class WXDLLIMPEXP_MATHPLOT mpPolygon : public mpMovableObject { +public: + /** Default constructor. + */ + mpPolygon(const wxString &layerName = wxT("")) { + m_continuous = true; + m_name = layerName; + } + + virtual ~mpPolygon() {} + + /** Set the points in the polygon. + * @param points_xs The X coordinates of the points. + * @param points_ys The Y coordinates of the points. + * @param closedShape If set to true, an additional segment will be added + * from the last to the first point. + */ + void setPoints(const std::vector &points_xs, + const std::vector &points_ys, bool closedShape = true); +}; + +//----------------------------------------------------------------------------- +// mpMovableObject - provided by Jose Luis Blanco +//----------------------------------------------------------------------------- +/** This virtual class represents objects that can be moved to an arbitrary 2D + * location+rotation. + * The current transformation is set through SetCoordinateBase. + * To ease the implementation of descendent classes, mpMovableObject will + * be in charge of Bounding Box computation and layer render, assuming that + * the object updates its shape in m_shape_xs & m_shape_ys. + */ +class WXDLLIMPEXP_MATHPLOT mpBitmapLayer : public mpLayer { +public: + /** Default constructor. + */ + mpBitmapLayer() { + m_min_x = m_max_x = m_min_y = m_max_y = 0; + m_validImg = false; + m_type = mpLAYER_BITMAP; + } + + virtual ~mpBitmapLayer(){}; + + /** Returns a copy of the current bitmap assigned to the layer. + */ + void GetBitmapCopy(wxImage &outBmp) const; + + /** Change the bitmap associated with the layer (to update the screen, refresh + * the mpWindow). + * @param inBmp The bitmap to associate. A copy is made, thus it can be + * released after calling this. + * @param x The left corner X coordinate (in plot units). + * @param y The top corner Y coordinate (in plot units). + * @param lx The width in plot units. + * @param ly The height in plot units. + */ + void SetBitmap(const wxImage &inBmp, double x, double y, double lx, + double ly); + + virtual bool HasBBox() { return true; } + + /** Get inclusive left border of bounding box. + */ + virtual double GetMinX() { return m_min_x; } + + /** Get inclusive right border of bounding box. + */ + virtual double GetMaxX() { return m_max_x; } + + /** Get inclusive bottom border of bounding box. + */ + virtual double GetMinY() { return m_min_y; } + + /** Get inclusive top border of bounding box. + */ + virtual double GetMaxY() { return m_max_y; } + + virtual void Plot(wxDC &dc, mpWindow &w); + + /** Set label axis alignment. + * @param align alignment (choose between mpALIGN_NE, mpALIGN_NW, + * mpALIGN_SW, mpALIGN_SE + */ + void SetAlign(int align) { m_flags = align; }; + +protected: + int m_flags; //!< Holds label alignment + + /** The internal copy of the Bitmap: + */ + wxImage m_bitmap; + wxBitmap m_scaledBitmap; + wxCoord m_scaledBitmap_offset_x, m_scaledBitmap_offset_y; + + bool m_validImg; + + /** The shape of the bitmap: + */ + double m_min_x, m_max_x, m_min_y, m_max_y; +}; + +/*@}*/ +#define DEFAULT_LINE_COLOUR_0 wxColour("#5B9BD5") +#define DEFAULT_LINE_COLOUR_1 wxColour("#ED7D31") +#define DEFAULT_LINE_COLOUR_2 wxColour("#FFC000") +#define DEFAULT_LINE_COLOUR_3 wxColour("#4472C4") +#define DEFAULT_LINE_COLOUR_4 wxColour("#70AD47") +#define DEFAULT_LINE_COLOUR_5 wxColour("#9E480E") +#define DEFAULT_LINE_COLOUR_6 wxColour("#997300") +#define DEFAULT_LINE_COLOUR_7 wxColour("#A5A5A5") + + +#define DEFAULT_TEXT_COLOUR wxColour("#686868") + +#define DEFAULT_AXIS_LABEL_COLOUR DEFAULT_TEXT_COLOUR +#define DEFAULT_AXIS_BORDER_COLOUR wxColour("#8B8B8B") +#define DEFAULT_AXIS_TICK_COLOUR wxColour("#8B8B8B") +#define DEFAULT_TITLE_COLOUR DEFAULT_TEXT_COLOUR + +#endif // _MP_MATHPLOT_H_ diff --git a/include/frm/menu.h b/include/frm/menu.h index e703170..7f0dbe8 100644 --- a/include/frm/menu.h +++ b/include/frm/menu.h @@ -81,6 +81,7 @@ enum MNU_COPY_WHERELIST, MNU_CLEAR_FILTER, MNU_CHECK_COLUMN_DATE, + MNU_AUTOCOLSPLOT, MNU_AUTOROLLBACK, MNU_AUTOCOMMIT, MNU_CLEARHISTORY, diff --git a/pgAdmin3.vcxproj b/pgAdmin3.vcxproj index 2995060..0e628d5 100644 --- a/pgAdmin3.vcxproj +++ b/pgAdmin3.vcxproj @@ -912,11 +912,13 @@ + + @@ -1549,6 +1551,8 @@ + + diff --git a/pgAdmin3.vcxproj.filters b/pgAdmin3.vcxproj.filters index ce053ba..c614b59 100644 --- a/pgAdmin3.vcxproj.filters +++ b/pgAdmin3.vcxproj.filters @@ -1650,6 +1650,12 @@ utils + + frm + + + frm + @@ -3548,6 +3554,12 @@ include\log + + include\frm + + + include\frm +